Merge "Fix panic gesture for hidden navigation bar"
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 2bd5aee..0fe34fb 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -311,6 +311,15 @@
compile_dex: true,
}
+java_defaults {
+ name: "android_stubs_dists_default",
+ dist: {
+ targets: ["sdk", "win_sdk"],
+ tag: ".jar",
+ dest: "android.jar",
+ },
+}
+
java_library_static {
name: "android_monolith_stubs_current",
srcs: [ ":api-stubs-docs" ],
@@ -346,7 +355,21 @@
name: "android_system_monolith_stubs_current",
srcs: [ ":system-api-stubs-docs" ],
static_libs: [ "private-stub-annotations-jar" ],
- defaults: ["android_defaults_stubs_current"],
+ defaults: [
+ "android_defaults_stubs_current",
+ "android_stubs_dists_default",
+ ],
+ dist: {
+ dir: "apistubs/android/system",
+ },
+ dists: [
+ {
+ // Legacy dist path
+ targets: ["sdk", "win_sdk"],
+ tag: ".jar",
+ dest: "android_system.jar",
+ },
+ ],
}
java_library_static {
@@ -378,14 +401,34 @@
name: "android_test_stubs_current",
srcs: [ ":test-api-stubs-docs" ],
static_libs: [ "private-stub-annotations-jar" ],
- defaults: ["android_defaults_stubs_current"],
+ defaults: [
+ "android_defaults_stubs_current",
+ "android_stubs_dists_default",
+ ],
+ dist: {
+ dir: "apistubs/android/test",
+ },
+ dists: [
+ {
+ // Legacy dist path
+ targets: ["sdk", "win_sdk"],
+ tag: ".jar",
+ dest: "android_test.jar",
+ },
+ ],
}
java_library_static {
name: "android_module_lib_stubs_current",
srcs: [ ":module-lib-api-stubs-docs-non-updatable" ],
- defaults: ["android_defaults_stubs_current"],
+ defaults: [
+ "android_defaults_stubs_current",
+ "android_stubs_dists_default",
+ ],
libs: ["sdk_system_29_android"],
+ dist: {
+ dir: "apistubs/android/module-lib",
+ },
}
java_library_static {
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java b/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
index 48ce8ab..8f8fc29e 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
@@ -26,7 +26,6 @@
import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
import org.junit.After;
@@ -37,7 +36,6 @@
/**
* Base class for all autofill tests.
*/
-@LargeTest
public abstract class AbstractAutofillPerfTestCase {
@ClassRule
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
index fb5ea80..5f52dc7 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
@@ -24,10 +24,14 @@
import android.view.View;
import android.widget.EditText;
+import androidx.test.filters.LargeTest;
+
import com.android.perftests.autofill.R;
+import org.junit.Ignore;
import org.junit.Test;
+@LargeTest
public class LoginTest extends AbstractAutofillPerfTestCase {
private EditText mUsername;
@@ -90,6 +94,8 @@
/**
* Now the service returns autofill data, for both username and password.
*/
+ // TODO(b/162216576): fix fail test and re-enable it
+ @Ignore
@Test
public void testFocus_autofillBothFields() throws Throwable {
MyAutofillService.newCannedResponse()
@@ -142,6 +148,8 @@
/**
* Now the service returns autofill data, but just for username.
*/
+ // TODO(b/162216576): fix fail test and re-enable it
+ @Ignore
@Test
public void testFocus_autofillUsernameOnly() throws Throwable {
// Must set ignored ids so focus on password does not trigger new requests
@@ -258,6 +266,8 @@
});
}
+ // TODO(b/162216576): fix fail test and re-enable it
+ @Ignore
@Test
public void testCallbacks() throws Throwable {
MyAutofillService.newCannedResponse()
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..9b853fe
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java
@@ -0,0 +1,267 @@
+/*
+ * 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.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.
+ */
+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..7257509
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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 androidx.test.filters.LargeTest;
+
+import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
+import com.android.perftests.contentcapture.R;
+
+import org.junit.Test;
+
+@LargeTest
+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/Android.bp b/apct-tests/perftests/core/Android.bp
index 984893a..03ab5b6 100644
--- a/apct-tests/perftests/core/Android.bp
+++ b/apct-tests/perftests/core/Android.bp
@@ -12,6 +12,8 @@
"androidx.appcompat_appcompat",
"androidx.test.rules",
"androidx.annotation_annotation",
+ "androidx.benchmark_benchmark-common",
+ "androidx.benchmark_benchmark-junit4",
"apct-perftests-overlay-apps",
"apct-perftests-resources-manager-apps",
"apct-perftests-utils",
diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml
index e9f4747..833ae63 100644
--- a/apct-tests/perftests/core/AndroidManifest.xml
+++ b/apct-tests/perftests/core/AndroidManifest.xml
@@ -34,7 +34,7 @@
</application>
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.benchmark.junit4.AndroidBenchmarkRunner"
android:targetPackage="com.android.perftests.core"/>
</manifest>
diff --git a/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java b/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java
index 14282bf..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.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
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";
@@ -67,22 +67,7 @@
+ "Z\n"
+ "@dp";
@Rule
- public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
-
- 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);
- }
-
+ public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
private static void toRectAndAddToRegion(Path p, Region inoutRegion, Rect inoutRect) {
final RectF rectF = new RectF();
@@ -168,19 +153,18 @@
@Test
public void parseByOldMethodForDoubleCutout() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ 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);
}
}
@Test
public void parseByNewMethodForDoubleCutout() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ 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);
}
}
@@ -209,10 +193,10 @@
+ "@right\n"
+ "@dp";
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ 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);
}
}
@@ -231,10 +215,10 @@
+ "Z\n"
+ "@dp";
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ 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/apct-tests/perftests/core/src/android/view/ViewPerfTest.java b/apct-tests/perftests/core/src/android/view/ViewPerfTest.java
index 72f02c4..a2aeb31 100644
--- a/apct-tests/perftests/core/src/android/view/ViewPerfTest.java
+++ b/apct-tests/perftests/core/src/android/view/ViewPerfTest.java
@@ -19,11 +19,11 @@
import static junit.framework.Assert.assertTrue;
import android.content.Context;
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.PerfTestActivity;
import android.widget.FrameLayout;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
@@ -37,7 +37,7 @@
@LargeTest
public class ViewPerfTest {
@Rule
- public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ public final BenchmarkRule mBenchmarkRule = new BenchmarkRule();
@Rule
public final ActivityTestRule<PerfTestActivity> mActivityRule =
@@ -52,7 +52,7 @@
@Test
public void testSimpleViewInflate() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
LayoutInflater inflater = LayoutInflater.from(mContext);
FrameLayout root = new FrameLayout(mContext);
while (state.keepRunning()) {
@@ -62,7 +62,7 @@
@Test
public void testTwelveKeyInflate() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
LayoutInflater inflater = LayoutInflater.from(mContext);
FrameLayout root = new FrameLayout(mContext);
while (state.keepRunning()) {
@@ -72,7 +72,7 @@
@Test
public void testPerformHapticFeedback() throws Throwable {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
mActivityRule.runOnUiThread(() -> {
state.pauseTiming();
View view = new View(mContext);
diff --git a/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java b/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
index b0edb11..a69d3ff 100644
--- a/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
+++ b/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
@@ -21,14 +21,14 @@
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.PerfTestActivity;
import android.view.View.MeasureSpec;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
@@ -50,7 +50,7 @@
new ActivityTestRule<>(PerfTestActivity.class);
@Rule
- public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ public final BenchmarkRule mBenchmarkRule = new BenchmarkRule();
public Context getContext() {
return InstrumentationRegistry.getInstrumentation().getTargetContext();
@@ -143,7 +143,7 @@
private void testParentWithChild(TestCallback callback) throws Throwable {
mActivityRule.runOnUiThread(() -> {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
FrameLayout parent = new FrameLayout(getContext());
mActivityRule.getActivity().setContentView(parent);
diff --git a/apct-tests/perftests/windowmanager/Android.bp b/apct-tests/perftests/windowmanager/Android.bp
index f02cbcf..9e95a10 100644
--- a/apct-tests/perftests/windowmanager/Android.bp
+++ b/apct-tests/perftests/windowmanager/Android.bp
@@ -19,6 +19,7 @@
"androidx.test.rules",
"androidx.annotation_annotation",
"apct-perftests-utils",
+ "platform-test-annotations",
],
test_suites: ["device-tests"],
platform_apis: true,
diff --git a/apct-tests/perftests/windowmanager/AndroidTest.xml b/apct-tests/perftests/windowmanager/AndroidTest.xml
index 69d187f..0a80cf9 100644
--- a/apct-tests/perftests/windowmanager/AndroidTest.xml
+++ b/apct-tests/perftests/windowmanager/AndroidTest.xml
@@ -21,9 +21,17 @@
<option name="test-file-name" value="WmPerfTests.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="force-skip-system-props" value="true" />
+ <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+ <option name="run-command" value="cmd window dismiss-keyguard" />
+ <option name="run-command" value="cmd package compile -m speed com.android.perftests.wm" />
+ </target_preparer>
+
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.perftests.wm" />
<option name="hidden-api-checks" value="false"/>
+ <option name="device-listeners" value="android.wm.WmPerfRunListener" />
</test>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/apct-tests/perftests/windowmanager/README.md b/apct-tests/perftests/windowmanager/README.md
new file mode 100644
index 0000000..05fa627
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/README.md
@@ -0,0 +1,27 @@
+## Window manager performance tests
+
+### Precondition
+To reduce the variance of the test, if `perf-setup.sh` (platform_testing/scripts/perf-setup)
+is available, it is better to use the following instructions to lock CPU and GPU frequencies.
+```
+m perf-setup.sh
+PERF_SETUP_PATH=/data/local/tmp/perf-setup.sh
+adb push $OUT/$PERF_SETUP_PATH $PERF_SETUP_PATH
+adb shell chmod +x $PERF_SETUP_PATH
+adb shell $PERF_SETUP_PATH
+```
+
+### Example to run
+Use `atest`
+```
+atest WmPerfTests:RelayoutPerfTest -- \
+ --module-arg WmPerfTests:instrumentation-arg:kill-bg:=true
+```
+Use `am instrument`
+```
+adb shell am instrument -w -r -e class android.wm.RelayoutPerfTest \
+ -e listener android.wm.WmPerfRunListener \
+ -e kill-bg true \
+ com.android.perftests.wm/androidx.test.runner.AndroidJUnitRunner
+```
+* `kill-bg` is optional.
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
index f04e555..cff5663 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
@@ -26,6 +26,7 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.PerfTestActivity;
+import android.platform.test.annotations.Presubmit;
import android.util.MergedConfiguration;
import android.view.DisplayCutout;
import android.view.IWindow;
@@ -52,6 +53,7 @@
@RunWith(Parameterized.class)
@LargeTest
+@Presubmit
public class RelayoutPerfTest extends WindowManagerPerfTestBase {
private int mIteration;
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
index 655d2f7..dc6245b 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
@@ -23,18 +23,15 @@
import android.app.UiAutomation;
import android.content.Context;
import android.content.Intent;
-import android.os.BatteryManager;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.perftests.utils.PerfTestActivity;
-import android.provider.Settings;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.lifecycle.ActivityLifecycleCallback;
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
import androidx.test.runner.lifecycle.Stage;
-import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
@@ -43,7 +40,9 @@
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
public class WindowManagerPerfTestBase {
static final UiAutomation sUiAutomation = getInstrumentation().getUiAutomation();
@@ -56,21 +55,11 @@
* is in /data because while enabling method profling of system server, it cannot write the
* trace to external storage.
*/
- static final File BASE_OUT_PATH = new File("/data/local/CorePerfTests");
-
- private static int sOriginalStayOnWhilePluggedIn;
+ static final File BASE_OUT_PATH = new File("/data/local/WmPerfTests");
@BeforeClass
public static void setUpOnce() {
final Context context = getInstrumentation().getContext();
- final int stayOnWhilePluggedIn = Settings.Global.getInt(context.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);
- }
if (!BASE_OUT_PATH.exists()) {
executeShellCommand("mkdir -p " + BASE_OUT_PATH);
@@ -84,18 +73,6 @@
.addCategory(Intent.CATEGORY_HOME).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
- @AfterClass
- public static void tearDownOnce() {
- if (sOriginalStayOnWhilePluggedIn != -1) {
- setStayOnWhilePluggedIn(sOriginalStayOnWhilePluggedIn);
- }
- }
-
- private static void setStayOnWhilePluggedIn(int value) {
- executeShellCommand(String.format("settings put global %s %d",
- Settings.Global.STAY_ON_WHILE_PLUGGED_IN, value));
- }
-
/**
* Executes shell command with reading the output. It may also used to block until the current
* command is completed.
@@ -124,6 +101,42 @@
executeShellCommand("am profile stop system");
}
+ static void runWithShellPermissionIdentity(Runnable runnable) {
+ sUiAutomation.adoptShellPermissionIdentity();
+ try {
+ runnable.run();
+ } finally {
+ sUiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ static class SettingsSession<T> implements AutoCloseable {
+ private final Consumer<T> mSetter;
+ private final T mOriginalValue;
+ private boolean mChanged;
+
+ SettingsSession(T originalValue, Consumer<T> setter) {
+ mOriginalValue = originalValue;
+ mSetter = setter;
+ }
+
+ void set(T value) {
+ if (Objects.equals(value, mOriginalValue)) {
+ mChanged = false;
+ return;
+ }
+ mSetter.accept(value);
+ mChanged = true;
+ }
+
+ @Override
+ public void close() {
+ if (mChanged) {
+ mSetter.accept(mOriginalValue);
+ }
+ }
+ }
+
/**
* Provides an activity that keeps screen on and is able to wait for a stable lifecycle stage.
*/
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java b/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java
new file mode 100644
index 0000000..6eb85aa
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.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 android.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.wm.WindowManagerPerfTestBase.executeShellCommand;
+import static android.wm.WindowManagerPerfTestBase.runWithShellPermissionIdentity;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.ActivityTaskManager;
+import android.content.Context;
+import android.os.BatteryManager;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.view.WindowManagerPolicyConstants;
+import android.wm.WindowManagerPerfTestBase.SettingsSession;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.policy.PhoneWindow;
+
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+import org.junit.runner.notification.RunListener;
+
+import java.util.List;
+
+/** Prepare the preconditions before running performance test. */
+public class WmPerfRunListener extends RunListener {
+
+ private static final String OPTION_KILL_BACKGROUND = "kill-bg";
+ private static final long KILL_BACKGROUND_WAIT_MS = 3000;
+
+ private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ private long mWaitPreconditionDoneMs = 500;
+
+ private final SettingsSession<Integer> mStayOnWhilePluggedInSetting = new SettingsSession<>(
+ Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0),
+ value -> executeShellCommand(String.format("settings put global %s %d",
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, value)));
+
+ private final SettingsSession<Integer> mNavigationModeSetting = new SettingsSession<>(
+ mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_navBarInteractionMode),
+ value -> {
+ final String navOverlay;
+ switch (value) {
+ case WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON:
+ navOverlay = WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;
+ break;
+ case WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON:
+ navOverlay = WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
+ break;
+ case WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL:
+ default:
+ navOverlay = WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY;
+ break;
+ }
+ executeShellCommand("cmd overlay enable-exclusive " + navOverlay);
+ });
+
+ /** It only executes once before all tests. */
+ @Override
+ public void testRunStarted(Description description) {
+ final Bundle arguments = InstrumentationRegistry.getArguments();
+
+ // Use gesture navigation for consistency.
+ mNavigationModeSetting.set(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL);
+ // Keep the device awake during testing.
+ mStayOnWhilePluggedInSetting.set(BatteryManager.BATTERY_PLUGGED_ANY);
+
+ runWithShellPermissionIdentity(() -> {
+ final ActivityTaskManager atm = mContext.getSystemService(ActivityTaskManager.class);
+ atm.removeAllVisibleRecentTasks();
+ atm.removeStacksWithActivityTypes(new int[] { ACTIVITY_TYPE_STANDARD,
+ ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS, ACTIVITY_TYPE_UNDEFINED });
+ });
+ PhoneWindow.sendCloseSystemWindows(mContext, "WmPerfTests");
+
+ if (Boolean.parseBoolean(arguments.getString(OPTION_KILL_BACKGROUND))) {
+ runWithShellPermissionIdentity(this::killBackgroundProcesses);
+ mWaitPreconditionDoneMs = KILL_BACKGROUND_WAIT_MS;
+ }
+ // Wait a while for the precondition setup to complete.
+ SystemClock.sleep(mWaitPreconditionDoneMs);
+ }
+
+ private void killBackgroundProcesses() {
+ final ActivityManager am = mContext.getSystemService(ActivityManager.class);
+ final List<RunningAppProcessInfo> processes = am.getRunningAppProcesses();
+ if (processes == null) {
+ return;
+ }
+ for (RunningAppProcessInfo processInfo : processes) {
+ if (processInfo.importanceReasonCode == RunningAppProcessInfo.REASON_UNKNOWN
+ && processInfo.importance > RunningAppProcessInfo.IMPORTANCE_SERVICE) {
+ for (String pkg : processInfo.pkgList) {
+ am.forceStopPackage(pkg);
+ }
+ }
+ }
+ }
+
+ /** It only executes once after all tests. */
+ @Override
+ public void testRunFinished(Result result) {
+ mNavigationModeSetting.close();
+ mStayOnWhilePluggedInSetting.close();
+ }
+}
diff --git a/apct-tests/perftests/windowmanager/src/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/apct-tests/perftests/windowmanager/src/com/android/server/wm/test/filters/FrameworksTestsFilter.java
new file mode 100644
index 0000000..d018287
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/src/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -0,0 +1,45 @@
+/*
+ * 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.wm.test.filters;
+
+import android.wm.RelayoutPerfTest;
+
+import org.junit.runner.Description;
+import org.junit.runner.manipulation.Filter;
+
+/**
+ * A static filter to have the same signature as the one in frameworks/base/tests/utils/testutils/.
+ * This doesn't share the existing library because it doesn't support parameterized test.
+ */
+public class FrameworksTestsFilter extends Filter {
+
+ private boolean mShouldRun;
+
+ @Override
+ public boolean shouldRun(Description description) {
+ final Class<?> testClass = description.getTestClass();
+ // Parameterized test methods don't have the original information. So keep the last status
+ // that matches the target class.
+ mShouldRun = (mShouldRun && testClass == null) || testClass == RelayoutPerfTest.class;
+ return mShouldRun;
+ }
+
+ @Override
+ public String describe() {
+ return "Default filter";
+ }
+}
diff --git a/apex/jobscheduler/framework/java/com/android/server/AppStateTracker.java b/apex/jobscheduler/framework/java/com/android/server/AppStateTracker.java
new file mode 100644
index 0000000..b0b9abc
--- /dev/null
+++ b/apex/jobscheduler/framework/java/com/android/server/AppStateTracker.java
@@ -0,0 +1,43 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+
+/**
+ * Tracks the forced-app-standby state for apps.
+ */
+public interface AppStateTracker {
+ String TAG = "AppStateTracker";
+
+ /**
+ * Register a {@link ServiceStateListener} to listen for forced-app-standby changes that should
+ * affect services.
+ */
+ void addServiceStateListener(@NonNull ServiceStateListener listener);
+
+ /**
+ * A listener to listen to forced-app-standby changes that should affect services.
+ */
+ interface ServiceStateListener {
+ /**
+ * Called when an app goes into forced app standby and its foreground
+ * services need to be removed from that state.
+ */
+ void stopForegroundServicesForUidPackage(int uid, String packageName);
+ }
+}
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/services/core/java/com/android/server/AppStateTracker.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
similarity index 95%
rename from services/core/java/com/android/server/AppStateTracker.java
rename to apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
index 7343352e..6d23635 100644
--- a/services/core/java/com/android/server/AppStateTracker.java
+++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
@@ -73,8 +73,7 @@
*
* Test: atest com.android.server.AppStateTrackerTest
*/
-public class AppStateTracker {
- private static final String TAG = "AppStateTracker";
+public class AppStateTrackerImpl implements AppStateTracker {
private static final boolean DEBUG = false;
private final Object mLock = new Object();
@@ -164,6 +163,16 @@
@GuardedBy("mLock")
boolean mForcedAppStandbyEnabled;
+ @Override
+ public void addServiceStateListener(@NonNull ServiceStateListener listener) {
+ addListener(new Listener() {
+ @Override
+ public void stopForegroundServicesForUidPackage(int uid, String packageName) {
+ listener.stopForegroundServicesForUidPackage(uid, packageName);
+ }
+ });
+ }
+
interface Stats {
int UID_FG_STATE_CHANGED = 0;
int UID_ACTIVE_STATE_CHANGED = 1;
@@ -228,7 +237,7 @@
}
mForcedAppStandbyEnabled = enabled;
if (DEBUG) {
- Slog.d(TAG,"Forced app standby feature flag changed: "
+ Slog.d(TAG, "Forced app standby feature flag changed: "
+ mForcedAppStandbyEnabled);
}
}
@@ -253,17 +262,20 @@
}
}
- public static abstract class Listener {
+ /**
+ * Listener for any state changes that affect any app's eligibility to run.
+ */
+ public abstract static class Listener {
/**
* This is called when the OP_RUN_ANY_IN_BACKGROUND appops changed for a package.
*/
- private void onRunAnyAppOpsChanged(AppStateTracker sender,
+ private void onRunAnyAppOpsChanged(AppStateTrackerImpl sender,
int uid, @NonNull String packageName) {
updateJobsForUidPackage(uid, packageName, sender.isUidActive(uid));
if (!sender.areAlarmsRestricted(uid, packageName, /*allowWhileIdle=*/ false)) {
unblockAlarmsForUidPackage(uid, packageName);
- } else if (!sender.areAlarmsRestricted(uid, packageName, /*allowWhileIdle=*/ true)){
+ } else if (!sender.areAlarmsRestricted(uid, packageName, /*allowWhileIdle=*/ true)) {
// we need to deliver the allow-while-idle alarms for this uid, package
unblockAllUnrestrictedAlarms();
}
@@ -278,14 +290,14 @@
/**
* This is called when the foreground state changed for a UID.
*/
- private void onUidForegroundStateChanged(AppStateTracker sender, int uid) {
+ private void onUidForegroundStateChanged(AppStateTrackerImpl sender, int uid) {
onUidForeground(uid, sender.isUidInForeground(uid));
}
/**
* This is called when the active/idle state changed for a UID.
*/
- private void onUidActiveStateChanged(AppStateTracker sender, int uid) {
+ private void onUidActiveStateChanged(AppStateTrackerImpl sender, int uid) {
final boolean isActive = sender.isUidActive(uid);
updateJobsForUid(uid, isActive);
@@ -298,7 +310,7 @@
/**
* This is called when an app-id(s) is removed from the power save whitelist.
*/
- private void onPowerSaveUnwhitelisted(AppStateTracker sender) {
+ private void onPowerSaveUnwhitelisted(AppStateTrackerImpl sender) {
updateAllJobs();
unblockAllUnrestrictedAlarms();
}
@@ -307,14 +319,14 @@
* This is called when the power save whitelist changes, excluding the
* {@link #onPowerSaveUnwhitelisted} case.
*/
- private void onPowerSaveWhitelistedChanged(AppStateTracker sender) {
+ private void onPowerSaveWhitelistedChanged(AppStateTrackerImpl sender) {
updateAllJobs();
}
/**
* This is called when the temp whitelist changes.
*/
- private void onTempPowerSaveWhitelistChanged(AppStateTracker sender) {
+ private void onTempPowerSaveWhitelistChanged(AppStateTrackerImpl sender) {
// TODO This case happens rather frequently; consider optimizing and update jobs
// only for affected app-ids.
@@ -327,7 +339,7 @@
/**
* This is called when the EXEMPT bucket is updated.
*/
- private void onExemptChanged(AppStateTracker sender) {
+ private void onExemptChanged(AppStateTrackerImpl sender) {
// This doesn't happen very often, so just re-evaluate all jobs / alarms.
updateAllJobs();
unblockAllUnrestrictedAlarms();
@@ -336,7 +348,7 @@
/**
* This is called when the global "force all apps standby" flag changes.
*/
- private void onForceAllAppsStandbyChanged(AppStateTracker sender) {
+ private void onForceAllAppsStandbyChanged(AppStateTrackerImpl sender) {
updateAllJobs();
if (!sender.isForceAllAppsStandbyEnabled()) {
@@ -401,14 +413,12 @@
/**
* Called when an ephemeral uid goes to the background, so its alarms need to be removed.
- *
- * @param uid
*/
public void removeAlarmsForUid(int uid) {
}
}
- public AppStateTracker(Context context, Looper looper) {
+ public AppStateTrackerImpl(Context context, Looper looper) {
mContext = context;
mHandler = new MyHandler(looper);
}
@@ -711,7 +721,7 @@
public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
int bucket, int reason) {
if (DEBUG) {
- Slog.d(TAG,"onAppIdleStateChanged: " + packageName + " u" + userId
+ Slog.d(TAG, "onAppIdleStateChanged: " + packageName + " u" + userId
+ (idle ? " idle" : " active") + " " + bucket);
}
synchronized (mLock) {
@@ -751,7 +761,7 @@
private static final int MSG_ON_UID_GONE = 13;
private static final int MSG_ON_UID_IDLE = 14;
- public MyHandler(Looper looper) {
+ MyHandler(Looper looper) {
super(looper);
}
@@ -831,7 +841,7 @@
return;
}
}
- final AppStateTracker sender = AppStateTracker.this;
+ final AppStateTrackerImpl sender = AppStateTrackerImpl.this;
long start = mStatLogger.getTime();
switch (msg.what) {
@@ -1077,7 +1087,7 @@
// Public interface.
/**
- * Register a new listener.
+ * Register a listener to get callbacks when any state changes.
*/
public void addListener(@NonNull Listener listener) {
synchronized (mLock) {
@@ -1127,8 +1137,7 @@
if (ArrayUtils.contains(mPowerWhitelistedAllAppIds, appId)) {
return false;
}
- if (useTempWhitelistToo &&
- ArrayUtils.contains(mTempWhitelistedAppIds, appId)) {
+ if (useTempWhitelistToo && ArrayUtils.contains(mTempWhitelistedAppIds, appId)) {
return false;
}
if (mForcedAppStandbyEnabled && isRunAnyRestrictedLocked(uid, packageName)) {
@@ -1197,7 +1206,6 @@
/**
* @return whether force all apps standby is enabled or not.
- *
*/
public boolean isForceAllAppsStandbyEnabled() {
synchronized (mLock) {
@@ -1248,11 +1256,18 @@
}
}
+ /**
+ * @deprecated use {@link #dump(IndentingPrintWriter)} instead.
+ */
@Deprecated
public void dump(PrintWriter pw, String prefix) {
dump(new IndentingPrintWriter(pw, " ").setIndent(prefix));
}
+ /**
+ * Dump the internal state to the given PrintWriter. Can be included in the dump
+ * of a binder service to be output on the shell command "dumpsys".
+ */
public void dump(IndentingPrintWriter pw) {
synchronized (mLock) {
pw.println("Forced App Standby Feature enabled: " + mForcedAppStandbyEnabled);
@@ -1329,6 +1344,9 @@
pw.println("]");
}
+ /**
+ * Proto version of {@link #dump(IndentingPrintWriter)}
+ */
public void dumpProto(ProtoOutputStream proto, long fieldId) {
synchronized (mLock) {
final long token = proto.start(fieldId);
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 7b681fb..b1bafee 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -286,7 +286,7 @@
private Intent mIdleIntent;
private Intent mLightIdleIntent;
private AnyMotionDetector mAnyMotionDetector;
- private final AppStateTracker mAppStateTracker;
+ private final AppStateTrackerImpl mAppStateTracker;
private boolean mLightEnabled;
private boolean mDeepEnabled;
private boolean mQuickDozeActivated;
@@ -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
@@ -1859,8 +1860,8 @@
return new AnyMotionDetector(getPowerManager(), handler, sm, callback, angleThreshold);
}
- AppStateTracker getAppStateTracker(Context ctx, Looper looper) {
- return new AppStateTracker(ctx, looper);
+ AppStateTrackerImpl getAppStateTracker(Context ctx, Looper looper) {
+ return new AppStateTrackerImpl(ctx, looper);
}
ConnectivityManager getConnectivityManager() {
diff --git a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
index d99830dc4..b8fef63 100644
--- a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
@@ -10,6 +10,15 @@
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
+ },
+ {
+ "name": "FrameworksMockingServicesTests",
+ "file_patterns": ["AppStateTrackerImpl\\.java"],
+ "options": [
+ {"include-filter": "com.android.server.AppStateTrackerTest"},
+ {"include-annotation": "android.platform.test.annotations.Presubmit"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ ]
}
],
"postsubmit": [
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 5ca3b33..6529503 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -99,7 +99,8 @@
import com.android.internal.util.StatLogger;
import com.android.server.AlarmManagerInternal;
import com.android.server.AppStateTracker;
-import com.android.server.AppStateTracker.Listener;
+import com.android.server.AppStateTrackerImpl;
+import com.android.server.AppStateTrackerImpl.Listener;
import com.android.server.DeviceIdleInternal;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
@@ -286,7 +287,7 @@
private final SparseArray<AlarmManager.AlarmClockInfo> mHandlerSparseAlarmClockArray =
new SparseArray<>();
- private AppStateTracker mAppStateTracker;
+ private AppStateTrackerImpl mAppStateTracker;
private boolean mAppStandbyParole;
/**
@@ -1593,7 +1594,8 @@
LocalServices.getService(AppStandbyInternal.class);
appStandbyInternal.addListener(new AppStandbyTracker());
- mAppStateTracker = LocalServices.getService(AppStateTracker.class);
+ mAppStateTracker =
+ (AppStateTrackerImpl) LocalServices.getService(AppStateTracker.class);
mAppStateTracker.addListener(mForceAppStandbyListener);
mClockReceiver.scheduleTimeTickEvent();
@@ -4393,8 +4395,7 @@
/**
* Tracking of app assignments to standby buckets
*/
- private final class AppStandbyTracker extends
- AppIdleStateChangeListener {
+ private final class AppStandbyTracker extends AppIdleStateChangeListener {
@Override
public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
boolean idle, int bucket, int reason) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index a67e928..06c469a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -86,6 +86,7 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.AppStateTracker;
+import com.android.server.AppStateTrackerImpl;
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
@@ -283,7 +284,7 @@
IBatteryStats mBatteryStats;
DeviceIdleInternal mLocalDeviceIdleController;
@VisibleForTesting
- AppStateTracker mAppStateTracker;
+ AppStateTrackerImpl mAppStateTracker;
final UsageStatsManagerInternal mUsageStats;
private final AppStandbyInternal mAppStandbyInternal;
@@ -1524,7 +1525,7 @@
controller.onSystemServicesReady();
}
- mAppStateTracker = Objects.requireNonNull(
+ mAppStateTracker = (AppStateTrackerImpl) Objects.requireNonNull(
LocalServices.getService(AppStateTracker.class));
// Register br for package removals and user removals.
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
index fd26b72..b632435 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -26,7 +26,8 @@
import android.util.proto.ProtoOutputStream;
import com.android.server.AppStateTracker;
-import com.android.server.AppStateTracker.Listener;
+import com.android.server.AppStateTrackerImpl;
+import com.android.server.AppStateTrackerImpl.Listener;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobStore;
@@ -56,12 +57,12 @@
static final int KNOWN_ACTIVE = 1;
static final int KNOWN_INACTIVE = 2;
- private final AppStateTracker mAppStateTracker;
+ private final AppStateTrackerImpl mAppStateTracker;
public BackgroundJobsController(JobSchedulerService service) {
super(service);
- mAppStateTracker = Objects.requireNonNull(
+ mAppStateTracker = (AppStateTrackerImpl) Objects.requireNonNull(
LocalServices.getService(AppStateTracker.class));
mAppStateTracker.addListener(mForceAppStandbyListener);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
index 361ebe5..227b827 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
@@ -18,28 +18,20 @@
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.AlarmManager.OnAlarmListener;
-import android.content.ContentResolver;
import android.content.Context;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
import android.os.WorkSource;
-import android.provider.Settings;
import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
import android.util.Log;
import android.util.Slog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.job.ConstantsProto;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateControllerProto;
@@ -63,9 +55,6 @@
/** Delay alarm tag for logging purposes */
private final String DELAY_TAG = "*job.delay*";
- private final Handler mHandler;
- private final TcConstants mTcConstants;
-
private long mNextJobExpiredElapsedMillis;
private long mNextDelayExpiredElapsedMillis;
@@ -81,14 +70,6 @@
mNextJobExpiredElapsedMillis = Long.MAX_VALUE;
mNextDelayExpiredElapsedMillis = Long.MAX_VALUE;
mChainedAttributionEnabled = mService.isChainedAttributionEnabled();
-
- mHandler = new Handler(mContext.getMainLooper());
- mTcConstants = new TcConstants(mHandler);
- }
-
- @Override
- public void onSystemServicesReady() {
- mTcConstants.start(mContext.getContentResolver());
}
/**
@@ -372,8 +353,7 @@
/**
* Set an alarm with the {@link android.app.AlarmManager} for the next time at which a job's
* delay will expire.
- * This alarm <b>will not</b> wake up the phone if
- * {@link TcConstants#USE_NON_WAKEUP_ALARM_FOR_DELAY} is true.
+ * This alarm <b>will not</b> wake up the phone.
*/
private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis, WorkSource ws) {
alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
@@ -381,10 +361,7 @@
return;
}
mNextDelayExpiredElapsedMillis = alarmTimeElapsedMillis;
- final int alarmType =
- mTcConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY
- ? AlarmManager.ELAPSED_REALTIME : AlarmManager.ELAPSED_REALTIME_WAKEUP;
- updateAlarmWithListenerLocked(DELAY_TAG, alarmType,
+ updateAlarmWithListenerLocked(DELAY_TAG, AlarmManager.ELAPSED_REALTIME,
mNextDelayExpiredListener, mNextDelayExpiredElapsedMillis, ws);
}
@@ -443,80 +420,6 @@
}
};
- @VisibleForTesting
- class TcConstants extends ContentObserver {
- private ContentResolver mResolver;
- private final KeyValueListParser mParser = new KeyValueListParser(',');
-
- private static final String KEY_USE_NON_WAKEUP_ALARM_FOR_DELAY =
- "use_non_wakeup_delay_alarm";
-
- private static final boolean DEFAULT_USE_NON_WAKEUP_ALARM_FOR_DELAY = true;
-
- /**
- * Whether or not TimeController should skip setting wakeup alarms for jobs that aren't
- * ready now.
- */
- public boolean USE_NON_WAKEUP_ALARM_FOR_DELAY = DEFAULT_USE_NON_WAKEUP_ALARM_FOR_DELAY;
-
- /**
- * Creates a content observer.
- *
- * @param handler The handler to run {@link #onChange} on, or null if none.
- */
- TcConstants(Handler handler) {
- super(handler);
- }
-
- private void start(ContentResolver resolver) {
- mResolver = resolver;
- mResolver.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS), false, this);
- onChange(true, null);
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- final String constants = Settings.Global.getString(
- mResolver, Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS);
-
- try {
- mParser.setString(constants);
- } catch (Exception e) {
- // Failed to parse the settings string, log this and move on with defaults.
- Slog.e(TAG, "Bad jobscheduler time controller settings", e);
- }
-
- USE_NON_WAKEUP_ALARM_FOR_DELAY = mParser.getBoolean(
- KEY_USE_NON_WAKEUP_ALARM_FOR_DELAY, DEFAULT_USE_NON_WAKEUP_ALARM_FOR_DELAY);
- // Intentionally not calling checkExpiredDelaysAndResetAlarm() here. There's no need to
- // iterate through the entire list again for this constant change. The next delay alarm
- // that is set will make use of the new constant value.
- }
-
- private void dump(IndentingPrintWriter pw) {
- pw.println();
- pw.println("TimeController:");
- pw.increaseIndent();
- pw.print(KEY_USE_NON_WAKEUP_ALARM_FOR_DELAY,
- USE_NON_WAKEUP_ALARM_FOR_DELAY).println();
- pw.decreaseIndent();
- }
-
- private void dump(ProtoOutputStream proto) {
- final long tcToken = proto.start(ConstantsProto.TIME_CONTROLLER);
- proto.write(ConstantsProto.TimeController.USE_NON_WAKEUP_ALARM_FOR_DELAY,
- USE_NON_WAKEUP_ALARM_FOR_DELAY);
- proto.end(tcToken);
- }
- }
-
- @VisibleForTesting
- @NonNull
- TcConstants getTcConstants() {
- return mTcConstants;
- }
-
@Override
public void dumpControllerStateLocked(IndentingPrintWriter pw,
Predicate<JobStatus> predicate) {
@@ -591,14 +494,4 @@
proto.end(mToken);
proto.end(token);
}
-
- @Override
- public void dumpConstants(IndentingPrintWriter pw) {
- mTcConstants.dump(pw);
- }
-
- @Override
- public void dumpConstants(ProtoOutputStream proto) {
- mTcConstants.dump(proto);
- }
}
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index f40f244..2f993da 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -487,9 +487,6 @@
mSystemServicesReady = true;
- // Offload to handler thread to avoid boot time impact.
- mHandler.post(AppStandbyController.this::updatePowerWhitelistCache);
-
boolean userFileExists;
synchronized (mAppIdleLock) {
userFileExists = mAppIdleHistory.userFileExists(UserHandle.USER_SYSTEM);
@@ -506,7 +503,9 @@
setChargingState(mInjector.isCharging());
// Offload to handler thread after boot completed to avoid boot time impact. This means
- // that headless system apps may be put in a lower bucket until boot has completed.
+ // that app standby buckets may be slightly out of date and headless system apps may be
+ // put in a lower bucket until boot has completed.
+ mHandler.post(AppStandbyController.this::updatePowerWhitelistCache);
mHandler.post(this::loadHeadlessSystemAppCache);
}
}
diff --git a/api/current.txt b/api/current.txt
index 651cda5..75ea7d7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5957,6 +5957,7 @@
method public long[] getVibrationPattern();
method public boolean hasUserSetImportance();
method public boolean hasUserSetSound();
+ method public boolean isDemoted();
method public boolean isImportantConversation();
method public void setAllowBubbles(boolean);
method public void setBypassDnd(boolean);
@@ -24079,6 +24080,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
@@ -27905,6 +27907,7 @@
field public static final java.util.UUID EFFECT_TYPE_DYNAMICS_PROCESSING;
field public static final java.util.UUID EFFECT_TYPE_ENV_REVERB;
field public static final java.util.UUID EFFECT_TYPE_EQUALIZER;
+ field @NonNull public static final java.util.UUID EFFECT_TYPE_HAPTIC_GENERATOR;
field public static final java.util.UUID EFFECT_TYPE_LOUDNESS_ENHANCER;
field public static final java.util.UUID EFFECT_TYPE_NS;
field public static final java.util.UUID EFFECT_TYPE_PRESET_REVERB;
@@ -28257,6 +28260,13 @@
field public short numBands;
}
+ public class HapticGenerator extends android.media.audiofx.AudioEffect implements java.lang.AutoCloseable {
+ method public void close();
+ method @NonNull public static android.media.audiofx.HapticGenerator create(int);
+ method public static boolean isAvailable();
+ method public int setEnabled(boolean);
+ }
+
public class LoudnessEnhancer extends android.media.audiofx.AudioEffect {
ctor public LoudnessEnhancer(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.RuntimeException, java.lang.UnsupportedOperationException;
method public float getTargetGain() throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.UnsupportedOperationException;
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/api/test-current.txt b/api/test-current.txt
index dc66265..866b9383 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -20,6 +20,7 @@
field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
+ field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS";
field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
@@ -83,6 +84,7 @@
method public static boolean isHighEndGfx();
method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void killProcessesWhenImperceptible(@NonNull int[], @NonNull String);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
+ method @RequiresPermission(android.Manifest.permission.RESET_APP_ERRORS) public void resetAppErrors();
method public static void resumeAppSwitches() throws android.os.RemoteException;
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int);
method @RequiresPermission("android.permission.MANAGE_USERS") public boolean switchUser(@NonNull android.os.UserHandle);
@@ -451,6 +453,7 @@
method public void lockFields(int);
method public void setBlockable(boolean);
method public void setDeleted(boolean);
+ method public void setDemoted(boolean);
method public void setFgServiceShown(boolean);
method public void setImportanceLockedByCriticalDeviceFunction(boolean);
method public void setImportanceLockedByOEM(boolean);
@@ -474,6 +477,7 @@
method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName);
method public boolean matchesCallFilter(android.os.Bundle);
method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean);
+ method public void updateNotificationChannel(@NonNull String, int, @NonNull android.app.NotificationChannel);
}
public final class PictureInPictureParams implements android.os.Parcelable {
@@ -3292,6 +3296,7 @@
field public static final String LOW_POWER_MODE_STICKY = "low_power_sticky";
field public static final String NOTIFICATION_BUBBLES = "notification_bubbles";
field public static final String OVERLAY_DISPLAY_DEVICES = "overlay_display_devices";
+ field public static final String SHOW_FIRST_CRASH_DIALOG = "show_first_crash_dialog";
field public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled";
field public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package";
}
@@ -3300,6 +3305,7 @@
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void resetToDefaults(@NonNull android.content.ContentResolver, @Nullable String);
field public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled";
field public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE = "accessibility_shortcut_target_service";
+ field public static final String ANR_SHOW_BACKGROUND = "anr_show_background";
field public static final String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
field public static final String AUTOFILL_SERVICE = "autofill_service";
field public static final String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count";
@@ -3320,6 +3326,7 @@
field public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
field public static final String NOTIFICATION_BADGING = "notification_badging";
field public static final String POWER_MENU_LOCKED_SHOW_CONTENT = "power_menu_locked_show_content";
+ field public static final String SHOW_FIRST_CRASH_DIALOG_DEV_OPTION = "show_first_crash_dialog_dev_option";
field public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard";
field @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
field public static final String USER_SETUP_COMPLETE = "user_setup_complete";
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 878cef9..e21a6b2 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -181,7 +181,6 @@
"idmap2/Dump.cpp",
"idmap2/Lookup.cpp",
"idmap2/Main.cpp",
- "idmap2/Scan.cpp",
],
target: {
android: {
diff --git a/cmds/idmap2/idmap2/Commands.h b/cmds/idmap2/idmap2/Commands.h
index 69eea8d..4099671 100644
--- a/cmds/idmap2/idmap2/Commands.h
+++ b/cmds/idmap2/idmap2/Commands.h
@@ -26,6 +26,5 @@
android::idmap2::Result<android::idmap2::Unit> CreateMultiple(const std::vector<std::string>& args);
android::idmap2::Result<android::idmap2::Unit> Dump(const std::vector<std::string>& args);
android::idmap2::Result<android::idmap2::Unit> Lookup(const std::vector<std::string>& args);
-android::idmap2::Result<android::idmap2::Unit> Scan(const std::vector<std::string>& args);
#endif // IDMAP2_IDMAP2_COMMANDS_H_
diff --git a/cmds/idmap2/idmap2/Main.cpp b/cmds/idmap2/idmap2/Main.cpp
index fb093f0..aa6d0e7 100644
--- a/cmds/idmap2/idmap2/Main.cpp
+++ b/cmds/idmap2/idmap2/Main.cpp
@@ -53,8 +53,10 @@
int main(int argc, char** argv) {
SYSTRACE << "main";
const NameToFunctionMap commands = {
- {"create", Create}, {"create-multiple", CreateMultiple}, {"dump", Dump}, {"lookup", Lookup},
- {"scan", Scan},
+ {"create", Create},
+ {"create-multiple", CreateMultiple},
+ {"dump", Dump},
+ {"lookup", Lookup},
};
if (argc <= 1) {
PrintUsage(commands, std::cerr);
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
deleted file mode 100644
index 3625045..0000000
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <dirent.h>
-
-#include <fstream>
-#include <memory>
-#include <ostream>
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "Commands.h"
-#include "android-base/properties.h"
-#include "idmap2/CommandLineOptions.h"
-#include "idmap2/CommandUtils.h"
-#include "idmap2/FileUtils.h"
-#include "idmap2/Idmap.h"
-#include "idmap2/Policies.h"
-#include "idmap2/PolicyUtils.h"
-#include "idmap2/ResourceUtils.h"
-#include "idmap2/Result.h"
-#include "idmap2/SysTrace.h"
-#include "idmap2/XmlParser.h"
-
-using android::idmap2::CommandLineOptions;
-using android::idmap2::Error;
-using android::idmap2::Idmap;
-using android::idmap2::Result;
-using android::idmap2::Unit;
-using android::idmap2::policy::kPolicyOdm;
-using android::idmap2::policy::kPolicyOem;
-using android::idmap2::policy::kPolicyProduct;
-using android::idmap2::policy::kPolicyPublic;
-using android::idmap2::policy::kPolicySystem;
-using android::idmap2::policy::kPolicyVendor;
-using android::idmap2::utils::ExtractOverlayManifestInfo;
-using android::idmap2::utils::FindFiles;
-using android::idmap2::utils::OverlayManifestInfo;
-using android::idmap2::utils::PoliciesToBitmaskResult;
-
-using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
-
-namespace {
-
-struct InputOverlay {
- bool operator<(InputOverlay const& rhs) const {
- return priority < rhs.priority || (priority == rhs.priority && apk_path < rhs.apk_path);
- }
-
- std::string apk_path; // NOLINT(misc-non-private-member-variables-in-classes)
- std::string idmap_path; // NOLINT(misc-non-private-member-variables-in-classes)
- int priority; // NOLINT(misc-non-private-member-variables-in-classes)
- std::vector<std::string> policies; // NOLINT(misc-non-private-member-variables-in-classes)
- bool ignore_overlayable; // NOLINT(misc-non-private-member-variables-in-classes)
-};
-
-bool VendorIsQOrLater() {
- constexpr int kQSdkVersion = 29;
- constexpr int kBase = 10;
- std::string version_prop = android::base::GetProperty("ro.vndk.version", "29");
- int version = strtol(version_prop.data(), nullptr, kBase);
-
- // If the string cannot be parsed, it is a development sdk codename.
- return version >= kQSdkVersion || version == 0;
-}
-
-Result<std::unique_ptr<std::vector<std::string>>> FindApkFiles(const std::vector<std::string>& dirs,
- bool recursive) {
- SYSTRACE << "FindApkFiles " << dirs << " " << recursive;
- const auto predicate = [](unsigned char type, const std::string& path) -> bool {
- static constexpr size_t kExtLen = 4; // strlen(".apk")
- return type == DT_REG && path.size() > kExtLen &&
- path.compare(path.size() - kExtLen, kExtLen, ".apk") == 0;
- };
- // pass apk paths through a set to filter out duplicates
- std::set<std::string> paths;
- for (const auto& dir : dirs) {
- const auto apk_paths = FindFiles(dir, recursive, predicate);
- if (!apk_paths) {
- return Error("failed to open directory %s", dir.c_str());
- }
- paths.insert(apk_paths->cbegin(), apk_paths->cend());
- }
- return std::make_unique<std::vector<std::string>>(paths.cbegin(), paths.cend());
-}
-
-std::vector<std::string> PoliciesForPath(const std::string& apk_path) {
- // clang-format off
- static const std::vector<std::pair<std::string, std::string>> values = {
- {"/odm/", kPolicyOdm},
- {"/oem/", kPolicyOem},
- {"/product/", kPolicyProduct},
- {"/system/", kPolicySystem},
- {"/system_ext/", kPolicySystem},
- {"/vendor/", kPolicyVendor},
- };
- // clang-format on
-
- std::vector<std::string> fulfilled_policies = {kPolicyPublic};
- for (auto const& pair : values) {
- if (apk_path.compare(0, pair.first.size(), pair.first) == 0) {
- fulfilled_policies.emplace_back(pair.second);
- break;
- }
- }
-
- return fulfilled_policies;
-}
-
-} // namespace
-
-Result<Unit> Scan(const std::vector<std::string>& args) {
- SYSTRACE << "Scan " << args;
- std::vector<std::string> input_directories;
- std::string target_package_name;
- std::string target_apk_path;
- std::string output_directory;
- std::vector<std::string> override_policies;
- bool recursive = false;
-
- const CommandLineOptions opts =
- CommandLineOptions("idmap2 scan")
- .MandatoryOption("--input-directory", "directory containing overlay apks to scan",
- &input_directories)
- .OptionalFlag("--recursive", "also scan subfolders of overlay-directory", &recursive)
- .MandatoryOption("--target-package-name", "package name of target package",
- &target_package_name)
- .MandatoryOption("--target-apk-path", "path to target apk", &target_apk_path)
- .MandatoryOption("--output-directory",
- "directory in which to write artifacts (idmap files and overlays.list)",
- &output_directory)
- .OptionalOption(
- "--override-policy",
- "input: an overlayable policy this overlay fulfills "
- "(if none or supplied, the overlays will not have their policies overriden",
- &override_policies);
- const auto opts_ok = opts.Parse(args);
- if (!opts_ok) {
- return opts_ok.GetError();
- }
-
- const auto apk_paths = FindApkFiles(input_directories, recursive);
- if (!apk_paths) {
- return Error(apk_paths.GetError(), "failed to find apk files");
- }
-
- std::vector<InputOverlay> interesting_apks;
- for (const std::string& path : **apk_paths) {
- Result<OverlayManifestInfo> overlay_info =
- ExtractOverlayManifestInfo(path, /* assert_overlay */ false);
- if (!overlay_info) {
- return overlay_info.GetError();
- }
-
- if (!overlay_info->is_static) {
- continue;
- }
-
- if (overlay_info->target_package.empty() ||
- overlay_info->target_package != target_package_name) {
- continue;
- }
-
- if (overlay_info->priority < 0) {
- continue;
- }
-
- // Note that conditional property enablement/exclusion only applies if
- // the attribute is present. In its absence, all overlays are presumed enabled.
- if (!overlay_info->requiredSystemPropertyName.empty() &&
- !overlay_info->requiredSystemPropertyValue.empty()) {
- // if property set & equal to value, then include overlay - otherwise skip
- if (android::base::GetProperty(overlay_info->requiredSystemPropertyName, "") !=
- overlay_info->requiredSystemPropertyValue) {
- continue;
- }
- }
-
- std::vector<std::string> fulfilled_policies;
- if (!override_policies.empty()) {
- fulfilled_policies = override_policies;
- } else {
- fulfilled_policies = PoliciesForPath(path);
- }
-
- bool ignore_overlayable = false;
- if (std::find(fulfilled_policies.begin(), fulfilled_policies.end(), kPolicyVendor) !=
- fulfilled_policies.end() &&
- !VendorIsQOrLater()) {
- // If the overlay is on a pre-Q vendor partition, do not enforce overlayable
- // restrictions on this overlay because the pre-Q platform has no understanding of
- // overlayable.
- ignore_overlayable = true;
- }
-
- std::string idmap_path = Idmap::CanonicalIdmapPathFor(output_directory, path);
-
- // Sort the static overlays in ascending priority order
- InputOverlay input{path, idmap_path, overlay_info->priority, fulfilled_policies,
- ignore_overlayable};
- interesting_apks.insert(
- std::lower_bound(interesting_apks.begin(), interesting_apks.end(), input), input);
- }
-
- std::stringstream stream;
- for (const auto& overlay : interesting_apks) {
- const auto policy_bitmask = PoliciesToBitmaskResult(overlay.policies);
- if (!policy_bitmask) {
- LOG(WARNING) << "failed to create idmap for overlay apk path \"" << overlay.apk_path
- << "\": " << policy_bitmask.GetErrorMessage();
- continue;
- }
-
- if (!Verify(overlay.idmap_path, target_apk_path, overlay.apk_path, *policy_bitmask,
- !overlay.ignore_overlayable)) {
- std::vector<std::string> create_args = {"--target-apk-path", target_apk_path,
- "--overlay-apk-path", overlay.apk_path,
- "--idmap-path", overlay.idmap_path};
- if (overlay.ignore_overlayable) {
- create_args.emplace_back("--ignore-overlayable");
- }
-
- for (const std::string& policy : overlay.policies) {
- create_args.emplace_back("--policy");
- create_args.emplace_back(policy);
- }
-
- const auto create_ok = Create(create_args);
- if (!create_ok) {
- LOG(WARNING) << "failed to create idmap for overlay apk path \"" << overlay.apk_path
- << "\": " << create_ok.GetError().GetMessage();
- continue;
- }
- }
-
- stream << overlay.idmap_path << std::endl;
- }
-
- std::cout << stream.str();
-
- return Unit{};
-}
diff --git a/cmds/idmap2/include/idmap2/FileUtils.h b/cmds/idmap2/include/idmap2/FileUtils.h
index 3f03236..c4e0e1f 100644
--- a/cmds/idmap2/include/idmap2/FileUtils.h
+++ b/cmds/idmap2/include/idmap2/FileUtils.h
@@ -17,27 +17,13 @@
#ifndef IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
#define IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
-#include <sys/types.h>
-
-#include <functional>
-#include <memory>
#include <string>
-#include <vector>
namespace android::idmap2::utils {
constexpr const char* kIdmapCacheDir = "/data/resource-cache";
constexpr const mode_t kIdmapFilePermissionMask = 0133; // u=rw,g=r,o=r
-typedef std::function<bool(unsigned char type /* DT_* from dirent.h */, const std::string& path)>
- FindFilesPredicate;
-std::unique_ptr<std::vector<std::string>> FindFiles(const std::string& root, bool recurse,
- const FindFilesPredicate& predicate);
-
-std::unique_ptr<std::string> ReadFile(int fd);
-
-std::unique_ptr<std::string> ReadFile(const std::string& path);
-
bool UidHasWriteAccessToPath(uid_t uid, const std::string& path);
} // namespace android::idmap2::utils
diff --git a/cmds/idmap2/libidmap2/FileUtils.cpp b/cmds/idmap2/libidmap2/FileUtils.cpp
index 3e8e329..3af1f70 100644
--- a/cmds/idmap2/libidmap2/FileUtils.cpp
+++ b/cmds/idmap2/libidmap2/FileUtils.cpp
@@ -16,19 +16,7 @@
#include "idmap2/FileUtils.h"
-#include <dirent.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <cerrno>
-#include <climits>
-#include <cstdlib>
-#include <cstring>
-#include <fstream>
-#include <memory>
#include <string>
-#include <utility>
-#include <vector>
#include "android-base/file.h"
#include "android-base/macros.h"
@@ -37,54 +25,6 @@
namespace android::idmap2::utils {
-std::unique_ptr<std::vector<std::string>> FindFiles(const std::string& root, bool recurse,
- const FindFilesPredicate& predicate) {
- DIR* dir = opendir(root.c_str());
- if (dir == nullptr) {
- return nullptr;
- }
- std::unique_ptr<std::vector<std::string>> vector(new std::vector<std::string>());
- struct dirent* dirent;
- while ((dirent = readdir(dir)) != nullptr) {
- const std::string path = root + "/" + dirent->d_name;
- if (predicate(dirent->d_type, path)) {
- vector->push_back(path);
- }
- if (recurse && dirent->d_type == DT_DIR && strcmp(dirent->d_name, ".") != 0 &&
- strcmp(dirent->d_name, "..") != 0) {
- auto sub_vector = FindFiles(path, recurse, predicate);
- if (!sub_vector) {
- closedir(dir);
- return nullptr;
- }
- vector->insert(vector->end(), sub_vector->begin(), sub_vector->end());
- }
- }
- closedir(dir);
-
- return vector;
-}
-
-std::unique_ptr<std::string> ReadFile(const std::string& path) {
- std::unique_ptr<std::string> str(new std::string());
- std::ifstream fin(path);
- str->append({std::istreambuf_iterator<char>(fin), std::istreambuf_iterator<char>()});
- fin.close();
- return str;
-}
-
-std::unique_ptr<std::string> ReadFile(int fd) {
- static constexpr const size_t kBufSize = 1024;
-
- std::unique_ptr<std::string> str(new std::string());
- char buf[kBufSize];
- ssize_t r;
- while ((r = read(fd, buf, sizeof(buf))) > 0) {
- str->append(buf, r);
- }
- return r == 0 ? std::move(str) : nullptr;
-}
-
#ifdef __ANDROID__
bool UidHasWriteAccessToPath(uid_t uid, const std::string& path) {
// resolve symlinks and relative paths; the directories must exist
diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp
index 8af4037..5750ca1 100644
--- a/cmds/idmap2/tests/FileUtilsTests.cpp
+++ b/cmds/idmap2/tests/FileUtilsTests.cpp
@@ -14,73 +14,16 @@
* limitations under the License.
*/
-#include <dirent.h>
-#include <fcntl.h>
-
-#include <set>
#include <string>
#include "TestHelpers.h"
-#include "android-base/macros.h"
#include "android-base/stringprintf.h"
-#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "idmap2/FileUtils.h"
#include "private/android_filesystem_config.h"
-using ::testing::NotNull;
-
namespace android::idmap2::utils {
-TEST(FileUtilsTests, FindFilesFindEverythingNonRecursive) {
- const auto& root = GetTestDataPath();
- auto v = utils::FindFiles(root, false,
- [](unsigned char type ATTRIBUTE_UNUSED,
- const std::string& path ATTRIBUTE_UNUSED) -> bool { return true; });
- ASSERT_THAT(v, NotNull());
- ASSERT_EQ(v->size(), 7U);
- ASSERT_EQ(std::set<std::string>(v->begin(), v->end()), std::set<std::string>({
- root + "/.",
- root + "/..",
- root + "/overlay",
- root + "/target",
- root + "/signature-overlay",
- root + "/system-overlay",
- root + "/system-overlay-invalid",
- }));
-}
-
-TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) {
- const auto& root = GetTestDataPath();
- auto v = utils::FindFiles(root, true, [](unsigned char type, const std::string& path) -> bool {
- return type == DT_REG && path.size() > 4 && path.compare(path.size() - 4, 4, ".apk") == 0;
- });
- ASSERT_THAT(v, NotNull());
- ASSERT_EQ(v->size(), 11U);
- ASSERT_EQ(std::set<std::string>(v->begin(), v->end()),
- std::set<std::string>(
- {root + "/target/target.apk", root + "/target/target-no-overlayable.apk",
- root + "/overlay/overlay.apk", root + "/overlay/overlay-no-name.apk",
- root + "/overlay/overlay-no-name-static.apk", root + "/overlay/overlay-shared.apk",
- root + "/overlay/overlay-static-1.apk", root + "/overlay/overlay-static-2.apk",
- root + "/signature-overlay/signature-overlay.apk",
- root + "/system-overlay/system-overlay.apk",
- root + "/system-overlay-invalid/system-overlay-invalid.apk"}));
-}
-
-TEST(FileUtilsTests, ReadFile) {
- int pipefd[2];
- ASSERT_EQ(pipe2(pipefd, O_CLOEXEC), 0);
-
- ASSERT_EQ(write(pipefd[1], "foobar", 6), 6);
- close(pipefd[1]);
-
- auto data = ReadFile(pipefd[0]);
- ASSERT_THAT(data, NotNull());
- ASSERT_EQ(*data, "foobar");
- close(pipefd[0]);
-}
-
#ifdef __ANDROID__
TEST(FileUtilsTests, UidHasWriteAccessToPath) {
constexpr const char* tmp_path = "/data/local/tmp/test@idmap";
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index d896cf9..61751b3 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -159,131 +159,6 @@
unlink(GetIdmapPath().c_str());
}
-TEST_F(Idmap2BinaryTests, Scan) {
- SKIP_TEST_IF_CANT_EXEC_IDMAP2;
-
- const std::string overlay_static_no_name_apk_path =
- GetTestDataPath() + "/overlay/overlay-no-name-static.apk";
- const std::string overlay_static_1_apk_path = GetTestDataPath() + "/overlay/overlay-static-1.apk";
- const std::string overlay_static_2_apk_path = GetTestDataPath() + "/overlay/overlay-static-2.apk";
- const std::string idmap_static_no_name_path =
- Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_no_name_apk_path);
- const std::string idmap_static_1_path =
- Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_1_apk_path);
- const std::string idmap_static_2_path =
- Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_2_apk_path);
-
- // single input directory, recursive
- // clang-format off
- auto result = ExecuteBinary({"idmap2",
- "scan",
- "--input-directory", GetTestDataPath(),
- "--recursive",
- "--target-package-name", "test.target",
- "--target-apk-path", GetTargetApkPath(),
- "--output-directory", GetTempDirPath(),
- "--override-policy", "public"});
- // clang-format on
- ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- std::stringstream expected;
- expected << idmap_static_no_name_path << std::endl;
- expected << idmap_static_1_path << std::endl;
- expected << idmap_static_2_path << std::endl;
- ASSERT_EQ(result->stdout, expected.str());
-
- auto idmap_static_no_name_raw_string = utils::ReadFile(idmap_static_no_name_path);
- auto idmap_static_no_name_raw_stream = std::istringstream(*idmap_static_no_name_raw_string);
- auto idmap_static_no_name = Idmap::FromBinaryStream(idmap_static_no_name_raw_stream);
- ASSERT_TRUE(idmap_static_no_name);
- ASSERT_IDMAP(**idmap_static_no_name, GetTargetApkPath(), overlay_static_no_name_apk_path);
-
- auto idmap_static_1_raw_string = utils::ReadFile(idmap_static_1_path);
- auto idmap_static_1_raw_stream = std::istringstream(*idmap_static_1_raw_string);
- auto idmap_static_1 = Idmap::FromBinaryStream(idmap_static_1_raw_stream);
- ASSERT_TRUE(idmap_static_1);
- ASSERT_IDMAP(**idmap_static_1, GetTargetApkPath(), overlay_static_1_apk_path);
-
- auto idmap_static_2_raw_string = utils::ReadFile(idmap_static_2_path);
- auto idmap_static_2_raw_stream = std::istringstream(*idmap_static_2_raw_string);
- auto idmap_static_2 = Idmap::FromBinaryStream(idmap_static_2_raw_stream);
- ASSERT_TRUE(idmap_static_2);
- ASSERT_IDMAP(**idmap_static_2, GetTargetApkPath(), overlay_static_2_apk_path);
-
- unlink(idmap_static_no_name_path.c_str());
- unlink(idmap_static_2_path.c_str());
- unlink(idmap_static_1_path.c_str());
-
- // multiple input directories, non-recursive
- // clang-format off
- result = ExecuteBinary({"idmap2",
- "scan",
- "--input-directory", GetTestDataPath() + "/target",
- "--input-directory", GetTestDataPath() + "/overlay",
- "--target-package-name", "test.target",
- "--target-apk-path", GetTargetApkPath(),
- "--output-directory", GetTempDirPath(),
- "--override-policy", "public"});
- // clang-format on
- ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- ASSERT_EQ(result->stdout, expected.str());
- unlink(idmap_static_no_name_path.c_str());
- unlink(idmap_static_2_path.c_str());
- unlink(idmap_static_1_path.c_str());
-
- // the same input directory given twice, but no duplicate entries
- // clang-format off
- result = ExecuteBinary({"idmap2",
- "scan",
- "--input-directory", GetTestDataPath(),
- "--input-directory", GetTestDataPath(),
- "--recursive",
- "--target-package-name", "test.target",
- "--target-apk-path", GetTargetApkPath(),
- "--output-directory", GetTempDirPath(),
- "--override-policy", "public"});
- // clang-format on
- ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- ASSERT_EQ(result->stdout, expected.str());
- unlink(idmap_static_no_name_path.c_str());
- unlink(idmap_static_2_path.c_str());
- unlink(idmap_static_1_path.c_str());
-
- // no APKs in input-directory: ok, but no output
- // clang-format off
- result = ExecuteBinary({"idmap2",
- "scan",
- "--input-directory", GetTempDirPath(),
- "--target-package-name", "test.target",
- "--target-apk-path", GetTargetApkPath(),
- "--output-directory", GetTempDirPath(),
- "--override-policy", "public"});
- // clang-format on
- ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- ASSERT_EQ(result->stdout, "");
-
- // the signature idmap failing to generate should not cause scanning to fail
- // clang-format off
- result = ExecuteBinary({"idmap2",
- "scan",
- "--input-directory", GetTestDataPath(),
- "--recursive",
- "--target-package-name", "test.target",
- "--target-apk-path", GetTargetApkPath(),
- "--output-directory", GetTempDirPath(),
- "--override-policy", "public"});
- // clang-format on
- ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- ASSERT_EQ(result->stdout, expected.str());
- unlink(idmap_static_no_name_path.c_str());
- unlink(idmap_static_2_path.c_str());
- unlink(idmap_static_1_path.c_str());
-}
-
TEST_F(Idmap2BinaryTests, Lookup) {
SKIP_TEST_IF_CANT_EXEC_IDMAP2;
diff --git a/cmds/idmap2/valgrind.sh b/cmds/idmap2/valgrind.sh
index b4ebab0..84daeec 100755
--- a/cmds/idmap2/valgrind.sh
+++ b/cmds/idmap2/valgrind.sh
@@ -53,7 +53,5 @@
_eval "idmap2 create" "$valgrind idmap2 create --policy public --target-apk-path $target_path --overlay-apk-path $overlay_path --idmap-path $idmap_path"
_eval "idmap2 dump" "$valgrind idmap2 dump --idmap-path $idmap_path"
_eval "idmap2 lookup" "$valgrind idmap2 lookup --idmap-path $idmap_path --config '' --resid test.target:string/str1"
-_eval "idmap2 scan" "$valgrind idmap2 scan --input-directory ${prefix}/tests/data/overlay --recursive --target-package-name test.target --target-apk-path $target_path --output-directory /tmp --override-policy public"
-_eval "idmap2 verify" "$valgrind idmap2 verify --idmap-path $idmap_path"
_eval "idmap2_tests" "$valgrind $ANDROID_HOST_OUT/nativetest64/idmap2_tests/idmap2_tests"
exit $errors
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index fc74fec..e70eac8 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -586,7 +586,7 @@
DataUsageBytesTransfer data_usage_bytes_transfer =
10082 [(module) = "framework", (truncate_timestamp) = true];
BytesTransferByTagAndMetered bytes_transfer_by_tag_and_metered =
- 10083 [(module) = "framework"];
+ 10083 [(module) = "framework", (truncate_timestamp) = true];
DNDModeProto dnd_mode_rule = 10084 [(module) = "framework"];
GeneralExternalStorageAccessStats general_external_storage_access_stats =
10085 [(module) = "mediaprovider"];
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 7c4c19d..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 {
@@ -4941,4 +4936,19 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Resets the state of the {@link com.android.server.am.AppErrors} instance.
+ * This is intended for use with CTS only.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.RESET_APP_ERRORS)
+ public void resetAppErrors() {
+ try {
+ getService().resetAppErrors();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 7ba50ca..ef30cb4 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -103,17 +103,16 @@
IBinder whitelistToken, long duration);
/**
- * Allows for a {@link PendingIntent} to be whitelisted to start activities from background.
+ * Allows a {@link PendingIntent} to start activities from background.
*/
public abstract void setPendingIntentAllowBgActivityStarts(
- IIntentSender target, IBinder whitelistToken, int flags);
+ IIntentSender target, IBinder allowlistToken, int flags);
/**
- * Voids {@link PendingIntent}'s privilege to be whitelisted to start activities from
- * background.
+ * Voids {@link PendingIntent}'s privilege to start activities from background.
*/
public abstract void clearPendingIntentAllowBgActivityStarts(IIntentSender target,
- IBinder whitelistToken);
+ IBinder allowlistToken);
/**
* Allow DeviceIdleController to tell us about what apps are whitelisted.
@@ -319,12 +318,14 @@
int uid, int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
- @UserIdInt int userId, boolean allowBackgroundActivityStarts);
+ @UserIdInt int userId, boolean allowBackgroundActivityStarts,
+ @Nullable IBinder backgroundActivityStartsToken);
public abstract ComponentName startServiceInPackage(int uid, Intent service,
String resolvedType, boolean fgRequired, String callingPackage,
@Nullable String callingFeatureId, @UserIdInt int userId,
- boolean allowBackgroundActivityStarts) throws TransactionTooLargeException;
+ boolean allowBackgroundActivityStarts,
+ @Nullable IBinder backgroundActivityStartsToken) throws TransactionTooLargeException;
public abstract void disconnectActivityFromServices(Object connectionHolder);
public abstract void cleanUpServices(@UserIdInt int userId, ComponentName component,
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/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index befd588..3b6a7b8 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -686,4 +686,11 @@
* Kills uid with the reason of permission change.
*/
void killUidForPermissionChange(int appId, int userId, String reason);
+
+ /**
+ * Resets the state of the {@link com.android.server.am.AppErrors} instance.
+ * This is intended for testing within the CTS only and is protected by
+ * android.permission.RESET_APP_ERRORS.
+ */
+ void resetAppErrors();
}
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/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index cf2f769..c827e60 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -828,12 +828,15 @@
/**
* @hide
*/
+ @TestApi
public void setDemoted(boolean demoted) {
mDemoted = demoted;
}
/**
- * @hide
+ * Returns whether the user has decided that this channel does not represent a conversation. The
+ * value will always be false for channels that never claimed to be conversations - that is,
+ * for channels where {@link #getConversationId()} and {@link #getParentChannelId()} are empty.
*/
public boolean isDemoted() {
return mDemoted;
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index eef9c02..8ee995d 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.
@@ -957,6 +958,20 @@
* @hide
*/
@TestApi
+ public void updateNotificationChannel(@NonNull String pkg, int uid,
+ @NonNull NotificationChannel channel) {
+ INotificationManager service = getService();
+ try {
+ service.updateNotificationChannelForPackage(pkg, uid, channel);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
public ComponentName getEffectsSuppressor() {
INotificationManager service = getService();
try {
@@ -1539,6 +1554,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/app/timezonedetector/ITimeZoneDetectorService.aidl b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
index d8675f3..6e93af6 100644
--- a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
+++ b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
@@ -41,6 +41,7 @@
TimeZoneConfiguration getConfiguration();
boolean updateConfiguration(in TimeZoneConfiguration configuration);
void addConfigurationListener(ITimeZoneConfigurationListener listener);
+ void removeConfigurationListener(ITimeZoneConfigurationListener listener);
boolean suggestManualTimeZone(in ManualTimeZoneSuggestion timeZoneSuggestion);
void suggestTelephonyTimeZone(in TelephonyTimeZoneSuggestion timeZoneSuggestion);
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index 621ef52..7885613 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -74,11 +74,26 @@
boolean updateConfiguration(@NonNull TimeZoneConfiguration configuration);
/**
+ * An interface that can be used to listen for changes to the time zone detector configuration.
+ */
+ interface TimeZoneConfigurationListener {
+ /** Called when the configuration changes. There are no guarantees about the thread used. */
+ void onChange(@NonNull TimeZoneConfiguration configuration);
+ }
+
+ /**
* Registers a listener that will be informed when the configuration changes. The complete
* configuration is passed to the listener, not just the properties that have changed.
*/
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
- void addConfigurationListener(@NonNull ITimeZoneConfigurationListener listener);
+ void addConfigurationListener(@NonNull TimeZoneConfigurationListener listener);
+
+ /**
+ * Removes a listener previously passed to
+ * {@link #addConfigurationListener(ITimeZoneConfigurationListener)}
+ */
+ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ void removeConfigurationListener(@NonNull TimeZoneConfigurationListener listener);
/**
* A shared utility method to create a {@link ManualTimeZoneSuggestion}.
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java
index 978cb21..6bd365f 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java
@@ -21,6 +21,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.ArraySet;
import android.util.Log;
/**
@@ -34,13 +35,16 @@
private final ITimeZoneDetectorService mITimeZoneDetectorService;
+ private ITimeZoneConfigurationListener mConfigurationReceiver;
+ private ArraySet<TimeZoneConfigurationListener> mConfigurationListeners;
+
public TimeZoneDetectorImpl() throws ServiceNotFoundException {
mITimeZoneDetectorService = ITimeZoneDetectorService.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.TIME_ZONE_DETECTOR_SERVICE));
}
@Override
- @NonNull
+ @NonNull
public TimeZoneCapabilities getCapabilities() {
if (DEBUG) {
Log.d(TAG, "getCapabilities called");
@@ -78,14 +82,70 @@
}
@Override
- public void addConfigurationListener(@NonNull ITimeZoneConfigurationListener listener) {
+ public void addConfigurationListener(@NonNull TimeZoneConfigurationListener listener) {
if (DEBUG) {
Log.d(TAG, "addConfigurationListener called: " + listener);
}
- try {
- mITimeZoneDetectorService.addConfigurationListener(listener);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ synchronized (this) {
+ if (mConfigurationListeners.contains(listener)) {
+ return;
+ }
+ if (mConfigurationReceiver == null) {
+ ITimeZoneConfigurationListener iListener =
+ new ITimeZoneConfigurationListener.Stub() {
+ @Override
+ public void onChange(@NonNull TimeZoneConfiguration configuration) {
+ notifyConfigurationListeners(configuration);
+ }
+ };
+ mConfigurationReceiver = iListener;
+ }
+ if (mConfigurationListeners == null) {
+ mConfigurationListeners = new ArraySet<>();
+ }
+
+ boolean wasEmpty = mConfigurationListeners.isEmpty();
+ mConfigurationListeners.add(listener);
+ if (wasEmpty) {
+ try {
+ mITimeZoneDetectorService.addConfigurationListener(mConfigurationReceiver);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+
+ private void notifyConfigurationListeners(@NonNull TimeZoneConfiguration configuration) {
+ ArraySet<TimeZoneConfigurationListener> configurationListeners;
+ synchronized (this) {
+ configurationListeners = new ArraySet<>(mConfigurationListeners);
+ }
+ int size = configurationListeners.size();
+ for (int i = 0; i < size; i++) {
+ configurationListeners.valueAt(i).onChange(configuration);
+ }
+ }
+
+ @Override
+ public void removeConfigurationListener(@NonNull TimeZoneConfigurationListener listener) {
+ if (DEBUG) {
+ Log.d(TAG, "removeConfigurationListener called: " + listener);
+ }
+
+ synchronized (this) {
+ if (mConfigurationListeners == null) {
+ return;
+ }
+ boolean wasEmpty = mConfigurationListeners.isEmpty();
+ mConfigurationListeners.remove(listener);
+ if (mConfigurationListeners.isEmpty() && !wasEmpty) {
+ try {
+ mITimeZoneDetectorService.removeConfigurationListener(mConfigurationReceiver);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
}
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/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 15a184f..62c7b85 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -1573,7 +1573,6 @@
private static native long nativeAssetGetLength(long assetPtr);
private static native long nativeAssetGetRemainingLength(long assetPtr);
- private static native String[] nativeCreateIdmapsForStaticOverlaysTargetingAndroid();
private static native @Nullable Map nativeGetOverlayableMap(long ptr,
@NonNull String packageName);
private static native @Nullable String nativeGetOverlayablesToString(long ptr,
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 15625cd..decf053 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -683,6 +683,8 @@
*<p>BACKWARD_COMPATIBLE devices capable of streaming concurrently with other devices as described by
* {@link android.hardware.camera2.CameraManager#getConcurrentCameraIds} have the
* following guaranteed streams (when streaming concurrently with other devices)</p>
+ * <p> Note: The sizes mentioned for these concurrent streams are the maximum sizes guaranteed
+ * to be supported. Sizes smaller than these, obtained by {@link StreamConfigurationMap#getOutputSizes} for a particular format, are supported as well. </p>
*
* <table>
* <tr><th colspan="5">Concurrent stream guaranteed configurations</th></tr>
@@ -696,7 +698,7 @@
* </table><br>
* </p>
*
- * <p> Devices which are not backwards-compatible, support a mandatory single stream of size sVGA with image format {@code DEPTH16} during concurrent operation.
+ * <p> Devices which are not backwards-compatible, support a mandatory single stream of size sVGA with image format {@code DEPTH16} during concurrent operation. </p>
*
* <p> For guaranteed concurrent stream configurations:</p>
* <p> sVGA refers to the camera device's maximum resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
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/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index fb44b0b..ba0636f 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -83,7 +83,7 @@
int isMicMuted();
// Input device vibrator control.
- void vibrate(int deviceId, in long[] pattern, int repeat, IBinder token);
+ void vibrate(int deviceId, in long[] pattern, in int[] amplitudes, int repeat, IBinder token);
void cancelVibrate(int deviceId, IBinder token);
void setPointerIconType(int typeId);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 72217a1..f0faeb0 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -54,8 +54,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
-import java.util.concurrent.Executor;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* Provides information about input devices and available key layouts.
@@ -1298,14 +1298,17 @@
public void vibrate(int uid, String opPkg, VibrationEffect effect,
String reason, AudioAttributes attributes) {
long[] pattern;
+ int[] amplitudes;
int repeat;
if (effect instanceof VibrationEffect.OneShot) {
VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect;
pattern = new long[] { 0, oneShot.getDuration() };
+ amplitudes = new int[] { 0, oneShot.getAmplitude() };
repeat = -1;
} else if (effect instanceof VibrationEffect.Waveform) {
VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
pattern = waveform.getTimings();
+ amplitudes = waveform.getAmplitudes();
repeat = waveform.getRepeatIndex();
} else {
// TODO: Add support for prebaked effects
@@ -1314,7 +1317,7 @@
}
try {
- mIm.vibrate(mDeviceId, pattern, repeat, mToken);
+ mIm.vibrate(mDeviceId, pattern, amplitudes, repeat, mToken);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
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/net/network-policy-restrictions.md b/core/java/android/net/network-policy-restrictions.md
index 63ce1a2..04c658c 100644
--- a/core/java/android/net/network-policy-restrictions.md
+++ b/core/java/android/net/network-policy-restrictions.md
@@ -1,11 +1,11 @@
# Data Saver vs Battery Saver
-The tables below show whether an app has network access while on background depending on the status of Data Saver mode, Battery Saver mode, and the app's whitelist on those restricted modes.
+The tables below show whether an app has network access while on background depending on the status of Data Saver mode, Battery Saver mode, and the app's allowlist on those restricted modes.
### How to read the tables
-The 2 topmost rows define the Battery Saver mode and whether the app is whitelisted or not for it.
-The 2 leftmost columns define the Data Saver mode and whether the app is whitelisted, not whitelisted, or blacklisted for it.
+The 2 topmost rows define the Battery Saver mode and whether the app is allowlisted or not for it.
+The 2 leftmost columns define the Data Saver mode and whether the app is allowlisted, not allowlisted, or denylisted for it.
The cells define the network status when the app is on background.
More specifically:
@@ -14,9 +14,9 @@
* **DS OFF**: Data Saver Mode is off
* **BS ON**: Battery Saver Mode is on
* **BS OFF**: Battery Saver Mode is off
-* **WL**: app is whitelisted
-* **!WL**: app is not whitelisted
-* **BL**: app is blacklisted
+* **AL**: app is allowlisted
+* **!AL**: app is not allowlisted
+* **DL**: app is denylisted
* **ok**: network access granted while app on background (NetworkInfo's state/detailed state should be `CONNECTED` / `CONNECTED`)
* **blk**: network access blocked while app on background (NetworkInfo's state/detailed state should be `DISCONNECTED` / `BLOCKED`)
@@ -25,23 +25,23 @@
| | | BS | ON | BS | OFF |
|:-------:|-------|------|-------|------|-------|
-| | | *WL* | *!WL* | *WL* | *!WL* |
-| **DS** | *WL* | ok | blk | ok | ok |
-| **ON** | *!WL* | blk | blk | blk | blk |
-| | *BL* | blk | blk | blk | blk |
-| **DS** | *WL* | blk | blk | ok | ok |
-| **OFF** | *!WL* | blk | blk | ok | ok |
-| | *BL* | blk | blk | blk | blk |
+| | | *AL* | *!AL* | *AL* | *!AL* |
+| **DS** | *AL* | ok | blk | ok | ok |
+| **ON** | *!AL* | blk | blk | blk | blk |
+| | *DL* | blk | blk | blk | blk |
+| **DS** | *AL* | blk | blk | ok | ok |
+| **OFF** | *!AL* | blk | blk | ok | ok |
+| | *DL* | blk | blk | blk | blk |
## On non-metered networks
| | | BS | ON | BS | OFF |
|:-------:|-------|------|-------|------|-------|
-| | | *WL* | *!WL* | *WL* | *!WL* |
-| **DS** | *WL* | ok | blk | ok | ok |
-| **ON** | *!WL* | ok | blk | ok | ok |
-| | *BL* | ok | blk | ok | ok |
-| **DS** | *WL* | ok | blk | ok | ok |
-| **OFF** | *!WL* | ok | blk | ok | ok |
-| | *BL* | ok | blk | ok | ok |
+| | | *AL* | *!AL* | *AL* | *!AL* |
+| **DS** | *AL* | ok | blk | ok | ok |
+| **ON** | *!AL* | ok | blk | ok | ok |
+| | *DL* | ok | blk | ok | ok |
+| **DS** | *AL* | ok | blk | ok | ok |
+| **OFF** | *!AL* | ok | blk | ok | ok |
+| | *DL* | ok | blk | ok | ok |
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/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index d2fc1d3..ce6c0ff 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -32,7 +32,6 @@
@UnsupportedAppUsage
void releaseWakeLock(IBinder lock, int flags);
void updateWakeLockUids(IBinder lock, in int[] uids);
- oneway void powerHint(int hintId, int data);
oneway void setPowerBoost(int boost, int durationMs);
oneway void setPowerMode(int mode, boolean enabled);
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/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 653a559..e30a409 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -196,12 +196,6 @@
public abstract void uidIdle(int uid);
/**
- * The hintId sent through this method should be in-line with the
- * PowerHint defined in android/hardware/power/<version 1.0 & up>/IPower.h
- */
- public abstract void powerHint(int hintId, int data);
-
- /**
* Boost: It is sent when user interacting with the device, for example,
* touchscreen events are incoming.
* Defined in hardware/interfaces/power/aidl/android/hardware/power/Boost.aidl
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 38ba47a..ff1bcf6 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -538,7 +538,7 @@
UserHandle other = (UserHandle)obj;
return mHandle == other.mHandle;
}
- } catch (ClassCastException e) {
+ } catch (ClassCastException ignore) {
}
return false;
}
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/provider/Settings.java b/core/java/android/provider/Settings.java
index e1d4886..346522a 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7614,6 +7614,8 @@
* @hide
*/
@UnsupportedAppUsage
+ @TestApi
+ @SuppressLint("NoSettingsProvider")
public static final String ANR_SHOW_BACKGROUND = "anr_show_background";
/**
@@ -7621,6 +7623,8 @@
* Otherwise, the process will be silently killed.
* @hide
*/
+ @TestApi
+ @SuppressLint("NoSettingsProvider")
public static final String SHOW_FIRST_CRASH_DIALOG_DEV_OPTION =
"show_first_crash_dialog_dev_option";
@@ -11862,21 +11866,6 @@
"job_scheduler_quota_controller_constants";
/**
- * Job scheduler TimeController specific settings.
- * This is encoded as a key=value list, separated by commas. Ex:
- *
- * "skip_not_ready_jobs=true5,other_key=2"
- *
- * <p>
- * Type: string
- *
- * @hide
- * @see com.android.server.job.JobSchedulerService.Constants
- */
- public static final String JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS =
- "job_scheduler_time_controller_constants";
-
- /**
* ShortcutManager specific settings.
* This is encoded as a key=value list, separated by commas. Ex:
*
@@ -14195,6 +14184,8 @@
* Otherwise, the process will be silently killed.
* @hide
*/
+ @TestApi
+ @SuppressLint("NoSettingsProvider")
public static final String SHOW_FIRST_CRASH_DIALOG = "show_first_crash_dialog";
/**
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 8ab120f..0a47032 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -107,7 +107,6 @@
IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() {
@Override
public void onSubscriptionsChanged () {
- Log.d(TAG, "onSubscriptionsChangedListener callback received.");
executor.execute(() -> listener.onSubscriptionsChanged());
}
};
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 683c747..38e3b39 100755
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -19,14 +19,13 @@
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.icu.text.DateFormatSymbols;
import android.icu.text.DateTimePatternGenerator;
import android.provider.Settings;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.SpannedString;
-import libcore.icu.LocaleData;
-
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
@@ -286,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");
}
/**
@@ -474,7 +475,8 @@
SpannableStringBuilder s = new SpannableStringBuilder(inFormat);
int count;
- LocaleData localeData = LocaleData.get(Locale.getDefault());
+ DateFormatSymbols dfs = getIcuDateFormatSymbols(Locale.getDefault());
+ String[] amPm = dfs.getAmPmStrings();
int len = inFormat.length();
@@ -496,14 +498,14 @@
switch (c) {
case 'A':
case 'a':
- replacement = localeData.amPm[inDate.get(Calendar.AM_PM) - Calendar.AM];
+ replacement = amPm[inDate.get(Calendar.AM_PM) - Calendar.AM];
break;
case 'd':
replacement = zeroPad(inDate.get(Calendar.DATE), count);
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)
@@ -531,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);
@@ -565,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);
@@ -678,4 +683,16 @@
private static String zeroPad(int inValue, int inMinDigits) {
return String.format(Locale.getDefault(), "%0" + inMinDigits + "d", inValue);
}
+
+ /**
+ * We use Gregorian calendar for date formats in android.text.format and various UI widget
+ * historically. It's a utility method to get an {@link DateFormatSymbols} instance. Note that
+ * {@link DateFormatSymbols} has cache, and external cache is not needed unless same instance is
+ * requested repeatedly in the performance critical code.
+ *
+ * @hide
+ */
+ public static DateFormatSymbols getIcuDateFormatSymbols(Locale locale) {
+ return new DateFormatSymbols(android.icu.util.GregorianCalendar.class, locale);
+ }
}
diff --git a/core/java/android/text/format/DateIntervalFormat.java b/core/java/android/text/format/DateIntervalFormat.java
new file mode 100644
index 0000000..de9ec7a
--- /dev/null
+++ b/core/java/android/text/format/DateIntervalFormat.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2013 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.text.format;
+
+import static android.text.format.DateUtils.FORMAT_SHOW_TIME;
+import static android.text.format.DateUtils.FORMAT_UTC;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
+import android.icu.util.Calendar;
+import android.icu.util.ULocale;
+import android.util.LruCache;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.text.FieldPosition;
+import java.util.TimeZone;
+
+/**
+ * A wrapper of {@link android.icu.text.DateIntervalFormat} used by {@link DateUtilsBridge}.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = PACKAGE)
+public final class DateIntervalFormat {
+
+ private static final LruCache<String, android.icu.text.DateIntervalFormat> CACHED_FORMATTERS =
+ new LruCache<>(8);
+
+ private DateIntervalFormat() {
+ }
+
+ /**
+ * Format a date range.
+ */
+ @VisibleForTesting(visibility = PACKAGE)
+ public static String formatDateRange(long startMs, long endMs, int flags, String olsonId) {
+ if ((flags & FORMAT_UTC) != 0) {
+ olsonId = "UTC";
+ }
+ // We create a java.util.TimeZone here to use libcore's data and libcore's olson ID /
+ // pseudo-tz logic.
+ TimeZone tz = (olsonId != null) ? TimeZone.getTimeZone(olsonId) : TimeZone.getDefault();
+ android.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz);
+ ULocale icuLocale = ULocale.getDefault();
+ return formatDateRange(icuLocale, icuTimeZone, startMs, endMs, flags);
+ }
+
+ /**
+ * Format a date range. This is our slightly more sensible internal API.
+ * A truly sane replacement would take a skeleton instead of int flags.
+ */
+ @VisibleForTesting(visibility = PACKAGE)
+ public static String formatDateRange(ULocale icuLocale, android.icu.util.TimeZone icuTimeZone,
+ long startMs, long endMs, int flags) {
+ Calendar startCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, startMs);
+ Calendar endCalendar;
+ if (startMs == endMs) {
+ endCalendar = startCalendar;
+ } else {
+ endCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, endMs);
+ }
+
+ // Special handling when the range ends at midnight:
+ // - If we're not showing times, and the range is non-empty, we fudge the end date so we
+ // don't count the day that's about to start.
+ // - If we are showing times, and the range ends at exactly 00:00 of the day following
+ // its start (which can be thought of as 24:00 the same day), we fudge the end date so we
+ // don't show the dates --- unless the start is anything displayed as 00:00, in which case
+ // we include both dates to disambiguate.
+ // This is not the behavior of icu4j's DateIntervalFormat, but it's the required behavior
+ // of Android's DateUtils.formatDateRange.
+ if (isExactlyMidnight(endCalendar)) {
+ boolean showTime = (flags & FORMAT_SHOW_TIME) == FORMAT_SHOW_TIME;
+ boolean endsDayAfterStart = DateUtilsBridge.dayDistance(startCalendar, endCalendar)
+ == 1;
+ if ((!showTime && startMs != endMs)
+ || (endsDayAfterStart
+ && !DateUtilsBridge.isDisplayMidnightUsingSkeleton(startCalendar))) {
+ endCalendar.add(Calendar.DAY_OF_MONTH, -1);
+ }
+ }
+
+ String skeleton = DateUtilsBridge.toSkeleton(startCalendar, endCalendar, flags);
+ synchronized (CACHED_FORMATTERS) {
+ android.icu.text.DateIntervalFormat formatter =
+ getFormatter(skeleton, icuLocale, icuTimeZone);
+ return formatter.format(startCalendar, endCalendar, new StringBuffer(),
+ new FieldPosition(0)).toString();
+ }
+ }
+
+ private static android.icu.text.DateIntervalFormat getFormatter(String skeleton, ULocale locale,
+ android.icu.util.TimeZone icuTimeZone) {
+ String key = skeleton + "\t" + locale + "\t" + icuTimeZone;
+ android.icu.text.DateIntervalFormat formatter = CACHED_FORMATTERS.get(key);
+ if (formatter != null) {
+ return formatter;
+ }
+ formatter = android.icu.text.DateIntervalFormat.getInstance(skeleton, locale);
+ formatter.setTimeZone(icuTimeZone);
+ CACHED_FORMATTERS.put(key, formatter);
+ return formatter;
+ }
+
+ private static boolean isExactlyMidnight(Calendar c) {
+ return c.get(Calendar.HOUR_OF_DAY) == 0
+ && c.get(Calendar.MINUTE) == 0
+ && c.get(Calendar.SECOND) == 0
+ && c.get(Calendar.MILLISECOND) == 0;
+ }
+}
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 51bea77..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,9 +28,6 @@
import com.android.internal.R;
-import libcore.icu.DateIntervalFormat;
-import libcore.icu.LocaleData;
-
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
@@ -204,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];
}
/**
@@ -226,7 +230,8 @@
*/
@Deprecated
public static String getAMPMString(int ampm) {
- return LocaleData.get(Locale.getDefault()).amPm[ampm - Calendar.AM];
+ String[] amPm = DateFormat.getIcuDateFormatSymbols(Locale.getDefault()).getAmPmStrings();
+ return amPm[ampm - Calendar.AM];
}
/**
@@ -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/DateUtilsBridge.java b/core/java/android/text/format/DateUtilsBridge.java
index 370d999..92ec9cf 100644
--- a/core/java/android/text/format/DateUtilsBridge.java
+++ b/core/java/android/text/format/DateUtilsBridge.java
@@ -16,6 +16,20 @@
package android.text.format;
+import static android.text.format.DateUtils.FORMAT_12HOUR;
+import static android.text.format.DateUtils.FORMAT_24HOUR;
+import static android.text.format.DateUtils.FORMAT_ABBREV_ALL;
+import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
+import static android.text.format.DateUtils.FORMAT_ABBREV_TIME;
+import static android.text.format.DateUtils.FORMAT_ABBREV_WEEKDAY;
+import static android.text.format.DateUtils.FORMAT_NO_MONTH_DAY;
+import static android.text.format.DateUtils.FORMAT_NO_YEAR;
+import static android.text.format.DateUtils.FORMAT_NUMERIC_DATE;
+import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
+import static android.text.format.DateUtils.FORMAT_SHOW_TIME;
+import static android.text.format.DateUtils.FORMAT_SHOW_WEEKDAY;
+import static android.text.format.DateUtils.FORMAT_SHOW_YEAR;
+
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import android.icu.util.Calendar;
@@ -33,24 +47,6 @@
*/
@VisibleForTesting(visibility = PACKAGE)
public final class DateUtilsBridge {
- // These are all public API in DateUtils. There are others, but they're either for use with
- // other methods (like FORMAT_ABBREV_RELATIVE), don't internationalize (like FORMAT_CAP_AMPM),
- // or have never been implemented anyway.
- public static final int FORMAT_SHOW_TIME = 0x00001;
- public static final int FORMAT_SHOW_WEEKDAY = 0x00002;
- public static final int FORMAT_SHOW_YEAR = 0x00004;
- public static final int FORMAT_NO_YEAR = 0x00008;
- public static final int FORMAT_SHOW_DATE = 0x00010;
- public static final int FORMAT_NO_MONTH_DAY = 0x00020;
- public static final int FORMAT_12HOUR = 0x00040;
- public static final int FORMAT_24HOUR = 0x00080;
- public static final int FORMAT_UTC = 0x02000;
- public static final int FORMAT_ABBREV_TIME = 0x04000;
- public static final int FORMAT_ABBREV_WEEKDAY = 0x08000;
- public static final int FORMAT_ABBREV_MONTH = 0x10000;
- public static final int FORMAT_NUMERIC_DATE = 0x20000;
- public static final int FORMAT_ABBREV_RELATIVE = 0x40000;
- public static final int FORMAT_ABBREV_ALL = 0x80000;
/**
* Creates an immutable ICU timezone backed by the specified libcore timezone data. At the time
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/RelativeDateTimeFormatter.java b/core/java/android/text/format/RelativeDateTimeFormatter.java
index c5bca17..9096469 100644
--- a/core/java/android/text/format/RelativeDateTimeFormatter.java
+++ b/core/java/android/text/format/RelativeDateTimeFormatter.java
@@ -16,14 +16,14 @@
package android.text.format;
-import static android.text.format.DateUtilsBridge.FORMAT_ABBREV_ALL;
-import static android.text.format.DateUtilsBridge.FORMAT_ABBREV_MONTH;
-import static android.text.format.DateUtilsBridge.FORMAT_ABBREV_RELATIVE;
-import static android.text.format.DateUtilsBridge.FORMAT_NO_YEAR;
-import static android.text.format.DateUtilsBridge.FORMAT_NUMERIC_DATE;
-import static android.text.format.DateUtilsBridge.FORMAT_SHOW_DATE;
-import static android.text.format.DateUtilsBridge.FORMAT_SHOW_TIME;
-import static android.text.format.DateUtilsBridge.FORMAT_SHOW_YEAR;
+import static android.text.format.DateUtils.FORMAT_ABBREV_ALL;
+import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
+import static android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE;
+import static android.text.format.DateUtils.FORMAT_NO_YEAR;
+import static android.text.format.DateUtils.FORMAT_NUMERIC_DATE;
+import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
+import static android.text.format.DateUtils.FORMAT_SHOW_TIME;
+import static android.text.format.DateUtils.FORMAT_SHOW_YEAR;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
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 cd541f2..c71dfbb 100644
--- a/core/java/android/text/format/TimeFormatter.java
+++ b/core/java/android/text/format/TimeFormatter.java
@@ -21,11 +21,11 @@
package android.text.format;
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;
@@ -52,15 +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 LocaleData sLocaleData;
+ private static DateFormatSymbols sDateFormatSymbols;
+ private static DecimalFormatSymbols sDecimalFormatSymbols;
private static String sTimeOnlyFormat;
private static String sDateOnlyFormat;
private static String sDateTimeFormat;
- private final LocaleData localeData;
+ private final DateFormatSymbols dateFormatSymbols;
+ private final DecimalFormatSymbols decimalFormatSymbols;
private final String dateTimeFormat;
private final String timeOnlyFormat;
private final String dateOnlyFormat;
@@ -74,7 +76,8 @@
if (sLocale == null || !(locale.equals(sLocale))) {
sLocale = locale;
- sLocaleData = LocaleData.get(locale);
+ sDateFormatSymbols = DateFormat.getIcuDateFormatSymbols(locale);
+ sDecimalFormatSymbols = DecimalFormatSymbols.getInstance(locale);
Resources r = Resources.getSystem();
sTimeOnlyFormat = r.getString(com.android.internal.R.string.time_of_day);
@@ -82,10 +85,11 @@
sDateTimeFormat = r.getString(com.android.internal.R.string.date_and_time);
}
+ this.dateFormatSymbols = sDateFormatSymbols;
+ this.decimalFormatSymbols = sDecimalFormatSymbols;
this.dateTimeFormat = sDateTimeFormat;
this.timeOnlyFormat = sTimeOnlyFormat;
this.dateOnlyFormat = sDateOnlyFormat;
- localeData = sLocaleData;
}
}
@@ -167,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);
@@ -215,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':
@@ -310,12 +323,14 @@
outputBuilder.append('\n');
return false;
case 'p':
- modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1]
- : localeData.amPm[0], modifier);
+ modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2))
+ ? dateFormatSymbols.getAmPmStrings()[1]
+ : dateFormatSymbols.getAmPmStrings()[0], modifier);
return false;
case 'P':
- modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1]
- : localeData.amPm[0], FORCE_LOWER_CASE);
+ modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2))
+ ? dateFormatSymbols.getAmPmStrings()[1]
+ : dateFormatSymbols.getAmPmStrings()[0], FORCE_LOWER_CASE);
return false;
case 'R':
formatInternal("%H:%M", wallTime, zoneInfoData);
diff --git a/core/java/android/text/method/NumberKeyListener.java b/core/java/android/text/method/NumberKeyListener.java
index d40015ee..2b038dd 100644
--- a/core/java/android/text/method/NumberKeyListener.java
+++ b/core/java/android/text/method/NumberKeyListener.java
@@ -29,8 +29,6 @@
import android.view.KeyEvent;
import android.view.View;
-import libcore.icu.LocaleData;
-
import java.util.Collection;
import java.util.Locale;
@@ -228,7 +226,7 @@
if (locale == null) {
return false;
}
- final String[] amPm = LocaleData.get(locale).amPm;
+ final String[] amPm = DateFormat.getIcuDateFormatSymbols(locale).getAmPmStrings();
for (int i = 0; i < amPm.length; i++) {
for (int j = 0; j < amPm[i].length(); j++) {
final char ch = amPm[i].charAt(j);
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/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 19eff72..51b0c6b 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -487,6 +487,21 @@
public static final int FLAG_TAINTED = 0x80000000;
/**
+ * Private flag indicating that this event was synthesized by the system and should be delivered
+ * to the accessibility focused view first. When being dispatched such an event is not handled
+ * by predecessors of the accessibility focused view and after the event reaches that view the
+ * flag is cleared and normal event dispatch is performed. This ensures that the platform can
+ * click on any view that has accessibility focus which is semantically equivalent to asking the
+ * view to perform a click accessibility action but more generic as views not implementing click
+ * action correctly can still be activated.
+ *
+ * @hide
+ * @see #isTargetAccessibilityFocus()
+ * @see #setTargetAccessibilityFocus(boolean)
+ */
+ public static final int FLAG_TARGET_ACCESSIBILITY_FOCUS = 0x40000000;
+
+ /**
* Flag indicating the motion event intersected the top edge of the screen.
*/
public static final int EDGE_TOP = 0x00000001;
@@ -2140,6 +2155,20 @@
}
/** @hide */
+ public boolean isTargetAccessibilityFocus() {
+ final int flags = getFlags();
+ return (flags & FLAG_TARGET_ACCESSIBILITY_FOCUS) != 0;
+ }
+
+ /** @hide */
+ public void setTargetAccessibilityFocus(boolean targetsFocus) {
+ final int flags = getFlags();
+ nativeSetFlags(mNativePtr, targetsFocus
+ ? flags | FLAG_TARGET_ACCESSIBILITY_FOCUS
+ : flags & ~FLAG_TARGET_ACCESSIBILITY_FOCUS);
+ }
+
+ /** @hide */
public final boolean isHoverExitPending() {
final int flags = getFlags();
return (flags & FLAG_HOVER_EXIT_PENDING) != 0;
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 361c46a..6beea876 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -49,7 +49,6 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.Trace;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseIntArray;
@@ -1455,6 +1454,22 @@
+ ", secure=" + secure
+ ", deviceProductInfo=" + deviceProductInfo + "}";
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DisplayInfo that = (DisplayInfo) o;
+ return isInternal == that.isInternal
+ && density == that.density
+ && secure == that.secure
+ && Objects.equals(deviceProductInfo, that.deviceProductInfo);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(isInternal, density, secure, deviceProductInfo);
+ }
}
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4e63a33..e951156 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -14391,6 +14391,14 @@
*/
public boolean dispatchTouchEvent(MotionEvent event) {
// If the event should be handled by accessibility focus first.
+ if (event.isTargetAccessibilityFocus()) {
+ // We don't have focus or no virtual descendant has it, do not handle the event.
+ if (!isAccessibilityFocusedViewOrHost()) {
+ return false;
+ }
+ // We have focus and got the event, then use normal event dispatch.
+ event.setTargetAccessibilityFocus(false);
+ }
boolean result = false;
if (mInputEventConsistencyVerifier != null) {
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 4a37f80..f648292 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2048,8 +2048,26 @@
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
+ View childWithAccessibilityFocus =
+ event.isTargetAccessibilityFocus()
+ ? findChildWithAccessibilityFocus()
+ : null;
+
if (!child.canReceivePointerEvents()
|| !isTransformedTouchPointInView(x, y, child, null)) {
+
+ // If there is a view that has accessibility focus we want it
+ // to get the event first and if not handled we will perform a
+ // normal dispatch. We may do a double iteration but this is
+ // safer given the timeframe.
+ if (childWithAccessibilityFocus != null) {
+ if (childWithAccessibilityFocus != child) {
+ continue;
+ }
+ childWithAccessibilityFocus = null;
+ i = childrenCount - 1;
+ }
+ event.setTargetAccessibilityFocus(false);
continue;
}
final PointerIcon pointerIcon =
@@ -2617,6 +2635,12 @@
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
+ // If the event targets the accessibility focused view and this is it, start
+ // normal event dispatch. Maybe a descendant is what will handle the click.
+ if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
+ ev.setTargetAccessibilityFocus(false);
+ }
+
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
@@ -2647,6 +2671,13 @@
// so this view group continues to intercept touches.
intercepted = true;
}
+
+ // If intercepted, start normal event dispatch. Also if there is already
+ // a view that is handling the gesture, do normal event dispatch.
+ if (intercepted || mFirstTouchTarget != null) {
+ ev.setTargetAccessibilityFocus(false);
+ }
+
// Check for cancelation.
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
@@ -2658,6 +2689,14 @@
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
+ // If the event is targeting accessibility focus we give it to the
+ // view that has accessibility focus and if it does not handle it
+ // we clear the flag and dispatch the event to all children as usual.
+ // We are looking up the accessibility focused host to avoid keeping
+ // state since these events are very rare.
+ View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
+ ? findChildWithAccessibilityFocus() : null;
+
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
@@ -2720,6 +2759,10 @@
alreadyDispatchedToNewTouchTarget = true;
break;
}
+
+ // The accessibility focus didn't handle the event, so clear
+ // the flag and do a normal dispatch to all children.
+ ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}
@@ -2803,6 +2846,34 @@
return buildOrderedChildList();
}
+ /**
+ * Finds the child which has accessibility focus.
+ *
+ * @return The child that has focus.
+ */
+ private View findChildWithAccessibilityFocus() {
+ ViewRootImpl viewRoot = getViewRootImpl();
+ if (viewRoot == null) {
+ return null;
+ }
+
+ View current = viewRoot.getAccessibilityFocusedHost();
+ if (current == null) {
+ return null;
+ }
+
+ ViewParent parent = current.getParent();
+ while (parent instanceof View) {
+ if (parent == this) {
+ return current;
+ }
+ current = (View) parent;
+ parent = current.getParent();
+ }
+
+ return null;
+ }
+
/**
* Resets all touch state in preparation for a new cycle.
*/
@@ -3258,8 +3329,8 @@
}
default:
throw new IllegalStateException("descendant focusability must be "
- + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
- + "but is " + descendantFocusability);
+ + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
+ + "but is " + descendantFocusability);
}
if (result && !isLayoutValid() && ((mPrivateFlags & PFLAG_WANTS_FOCUS) == 0)) {
mPrivateFlags |= PFLAG_WANTS_FOCUS;
@@ -4923,7 +4994,8 @@
if (params == null) {
params = generateDefaultLayoutParams();
if (params == null) {
- throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
+ throw new IllegalArgumentException(
+ "generateDefaultLayoutParams() cannot return null ");
}
}
addView(child, index, params);
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/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 5d50515..708e277 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -1768,7 +1768,7 @@
* <strong>Note:</strong> The primary usage of this API is for UI test automation
* and in order to report the fully qualified view id if an {@link AccessibilityNodeInfo}
* the client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS}
- * flag when configuring his {@link android.accessibilityservice.AccessibilityService}.
+ * flag when configuring their {@link android.accessibilityservice.AccessibilityService}.
* </p>
* <p>
* <strong>Note:</strong> If this view hierarchy has a {@link SurfaceView} embedding another
@@ -3206,7 +3206,7 @@
* <strong>Note:</strong> The primary usage of this API is for UI test automation
* and in order to report the source view id of an {@link AccessibilityNodeInfo} the
* client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS}
- * flag when configuring his {@link android.accessibilityservice.AccessibilityService}.
+ * flag when configuring their {@link android.accessibilityservice.AccessibilityService}.
* </p>
* @return The id resource name.
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/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 07fef76..c5f2299 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -27,6 +27,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
+import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
import android.util.Printer;
@@ -567,7 +568,8 @@
* editor wants to trim out the first 10 chars, subTextStart should be 10.
*/
public void setInitialSurroundingSubText(@NonNull CharSequence subText, int subTextStart) {
- Objects.requireNonNull(subText);
+ CharSequence newSubText = Editable.Factory.getInstance().newEditable(subText);
+ Objects.requireNonNull(newSubText);
// Swap selection start and end if necessary.
final int subTextSelStart = initialSelStart > initialSelEnd
@@ -575,7 +577,7 @@
final int subTextSelEnd = initialSelStart > initialSelEnd
? initialSelStart - subTextStart : initialSelEnd - subTextStart;
- final int subTextLength = subText.length();
+ final int subTextLength = newSubText.length();
// Unknown or invalid selection.
if (subTextStart < 0 || subTextSelStart < 0 || subTextSelEnd > subTextLength) {
mInitialSurroundingText = new InitialSurroundingText();
@@ -589,12 +591,12 @@
}
if (subTextLength <= MEMORY_EFFICIENT_TEXT_LENGTH) {
- mInitialSurroundingText = new InitialSurroundingText(subText, subTextSelStart,
+ mInitialSurroundingText = new InitialSurroundingText(newSubText, subTextSelStart,
subTextSelEnd);
return;
}
- trimLongSurroundingText(subText, subTextSelStart, subTextSelEnd);
+ trimLongSurroundingText(newSubText, subTextSelStart, subTextSelEnd);
}
/**
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/Editor.java b/core/java/android/widget/Editor.java
index 346bbb6..c4eb396 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -6053,9 +6053,8 @@
return trueLine;
}
- final int lineHeight = layout.getLineBottom(prevLine) - layout.getLineTop(prevLine);
- int slop = (int)(mLineSlopRatio
- * (layout.getLineBottom(trueLine) - layout.getLineTop(trueLine)));
+ final int lineHeight = mTextView.getLineHeight();
+ int slop = (int)(mLineSlopRatio * lineHeight);
slop = Math.max(mLineChangeSlopMin,
Math.min(mLineChangeSlopMax, lineHeight + slop)) - lineHeight;
slop = Math.max(0, slop);
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/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 51b1847..1c219eb 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -24,9 +24,11 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
+import android.icu.text.DateFormatSymbols;
import android.icu.util.Calendar;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.util.Log;
import android.util.MathUtils;
@@ -39,8 +41,6 @@
import com.android.internal.R;
-import libcore.icu.LocaleData;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Locale;
@@ -421,11 +421,13 @@
static String[] getAmPmStrings(Context context) {
final Locale locale = context.getResources().getConfiguration().locale;
- final LocaleData d = LocaleData.get(locale);
+ DateFormatSymbols dfs = DateFormat.getIcuDateFormatSymbols(locale);
+ String[] amPm = dfs.getAmPmStrings();
+ String[] narrowAmPm = dfs.getAmpmNarrowStrings();
final String[] result = new String[2];
- result[0] = d.amPm[0].length() > 4 ? d.narrowAm : d.amPm[0];
- result[1] = d.amPm[1].length() > 4 ? d.narrowPm : d.amPm[1];
+ result[0] = amPm[0].length() > 4 ? narrowAmPm[0] : amPm[0];
+ result[1] = amPm[1].length() > 4 ? narrowAmPm[1] : amPm[1];
return result;
}
diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java
index 83c86d5..bd2fa59 100644
--- a/core/java/android/widget/TimePickerSpinnerDelegate.java
+++ b/core/java/android/widget/TimePickerSpinnerDelegate.java
@@ -35,8 +35,6 @@
import com.android.internal.R;
-import libcore.icu.LocaleData;
-
import java.util.Calendar;
/**
@@ -143,7 +141,7 @@
mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
// Get the localized am/pm strings and use them in the spinner.
- mAmPmStrings = getAmPmStrings(context);
+ mAmPmStrings = TimePicker.getAmPmStrings(context);
// am/pm
final View amPmView = mDelegator.findViewById(R.id.amPm);
@@ -574,12 +572,4 @@
target.setContentDescription(mContext.getString(contDescResId));
}
}
-
- public static String[] getAmPmStrings(Context context) {
- String[] result = new String[2];
- LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
- result[0] = d.amPm[0].length() > 4 ? d.narrowAm : d.amPm[0];
- result[1] = d.amPm[1].length() > 4 ? d.narrowPm : d.amPm[1];
- return result;
- }
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index fbf050a..3a89dcd 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -3134,6 +3134,7 @@
// ends up disabled. That's because at some point the old tab's vertical scrolling is
// disabled and the new tab's is enabled. For context, see b/159997845
setVerticalScrollEnabled(true);
+ mResolverDrawerLayout.scrollNestedScrollableChildBackToTop();
}
@Override
diff --git a/core/java/com/android/internal/app/ProcessMap.java b/core/java/com/android/internal/app/ProcessMap.java
index 81036f7..719c79b 100644
--- a/core/java/com/android/internal/app/ProcessMap.java
+++ b/core/java/com/android/internal/app/ProcessMap.java
@@ -58,4 +58,8 @@
public int size() {
return mMap.size();
}
+
+ public void clear() {
+ mMap.clear();
+ }
}
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/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index dc8d57a..a2de0af 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -371,7 +371,7 @@
}
if (haveLast) {
canvas.drawLine(lastX, lastY, x, y, mPathPaint);
- final Paint paint = ps.mTraceCurrent[i] ? mCurrentPointPaint : mPaint;
+ final Paint paint = ps.mTraceCurrent[i - 1] ? mCurrentPointPaint : mPaint;
canvas.drawPoint(lastX, lastY, paint);
drawn = true;
}
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 3f708f8..90eeabb 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -464,11 +464,7 @@
smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, yvel);
mDismissOnScrollerFinished = true;
} else {
- if (isNestedListChildScrolled()) {
- mNestedListChild.smoothScrollToPosition(0);
- } else if (isNestedRecyclerChildScrolled()) {
- mNestedRecyclerChild.smoothScrollToPosition(0);
- }
+ scrollNestedScrollableChildBackToTop();
smoothScrollTo(yvel < 0 ? 0 : mCollapsibleHeight, yvel);
}
}
@@ -493,6 +489,17 @@
return handled;
}
+ /**
+ * Scroll nested scrollable child back to top if it has been scrolled.
+ */
+ public void scrollNestedScrollableChildBackToTop() {
+ if (isNestedListChildScrolled()) {
+ mNestedListChild.smoothScrollToPosition(0);
+ } else if (isNestedRecyclerChildScrolled()) {
+ mNestedRecyclerChild.smoothScrollToPosition(0);
+ }
+ }
+
private void onSecondaryPointerUp(MotionEvent ev) {
final int pointerIndex = ev.getActionIndex();
final int pointerId = ev.getPointerId(pointerIndex);
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/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index e6881b3..efca33a 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -39,7 +39,6 @@
#include "androidfw/AssetManager2.h"
#include "androidfw/AttributeResolution.h"
#include "androidfw/MutexGuard.h"
-#include "androidfw/PosixUtils.h"
#include "androidfw/ResourceTypes.h"
#include "androidfw/ResourceUtils.h"
@@ -58,7 +57,6 @@
extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
using ::android::base::StringPrintf;
-using ::android::util::ExecuteBinary;
namespace android {
@@ -114,88 +112,6 @@
return cookie > 0 ? static_cast<ApkAssetsCookie>(cookie - 1) : kInvalidCookie;
}
-static jobjectArray NativeCreateIdmapsForStaticOverlaysTargetingAndroid(JNIEnv* env,
- jclass /*clazz*/) {
- // --input-directory can be given multiple times, but idmap2 expects the directory to exist
- std::vector<std::string> input_dirs;
- struct stat st;
- if (stat(AssetManager::VENDOR_OVERLAY_DIR, &st) == 0) {
- input_dirs.push_back(AssetManager::VENDOR_OVERLAY_DIR);
- }
-
- if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) {
- input_dirs.push_back(AssetManager::PRODUCT_OVERLAY_DIR);
- }
-
- if (stat(AssetManager::SYSTEM_EXT_OVERLAY_DIR, &st) == 0) {
- input_dirs.push_back(AssetManager::SYSTEM_EXT_OVERLAY_DIR);
- }
-
- if (stat(AssetManager::ODM_OVERLAY_DIR, &st) == 0) {
- input_dirs.push_back(AssetManager::ODM_OVERLAY_DIR);
- }
-
- if (stat(AssetManager::OEM_OVERLAY_DIR, &st) == 0) {
- input_dirs.push_back(AssetManager::OEM_OVERLAY_DIR);
- }
-
- if (input_dirs.empty()) {
- LOG(WARNING) << "no directories for idmap2 to scan";
- return env->NewObjectArray(0, g_stringClass, nullptr);
- }
-
- if (access("/system/bin/idmap2", X_OK) == -1) {
- PLOG(WARNING) << "unable to execute idmap2";
- return nullptr;
- }
-
- std::vector<std::string> argv{"/system/bin/idmap2",
- "scan",
- "--recursive",
- "--target-package-name", "android",
- "--target-apk-path", "/system/framework/framework-res.apk",
- "--output-directory", "/data/resource-cache"};
-
- for (const auto& dir : input_dirs) {
- argv.push_back("--input-directory");
- argv.push_back(dir);
- }
-
- const auto result = ExecuteBinary(argv);
-
- if (!result) {
- LOG(ERROR) << "failed to execute idmap2";
- return nullptr;
- }
-
- if (result->status != 0) {
- LOG(ERROR) << "idmap2: " << result->stderr;
- return nullptr;
- }
-
- std::vector<std::string> idmap_paths;
- std::istringstream input(result->stdout);
- std::string path;
- while (std::getline(input, path)) {
- idmap_paths.push_back(path);
- }
-
- jobjectArray array = env->NewObjectArray(idmap_paths.size(), g_stringClass, nullptr);
- if (array == nullptr) {
- return nullptr;
- }
- for (size_t i = 0; i < idmap_paths.size(); i++) {
- const std::string path = idmap_paths[i];
- jstring java_string = env->NewStringUTF(path.c_str());
- if (env->ExceptionCheck()) {
- return nullptr;
- }
- env->SetObjectArrayElement(array, i, java_string);
- env->DeleteLocalRef(java_string);
- }
- return array;
-}
-
static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref,
uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) {
env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType);
@@ -1563,8 +1479,6 @@
{"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength},
// System/idmap related methods.
- {"nativeCreateIdmapsForStaticOverlaysTargetingAndroid", "()[Ljava/lang/String;",
- (void*)NativeCreateIdmapsForStaticOverlaysTargetingAndroid},
{"nativeGetOverlayableMap", "(JLjava/lang/String;)Ljava/util/Map;",
(void*)NativeGetOverlayableMap},
{"nativeGetOverlayablesToString", "(JLjava/lang/String;)Ljava/lang/String;",
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 8153166..2436b23 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -43,25 +43,24 @@
class NativeInputChannel {
public:
- explicit NativeInputChannel(const sp<InputChannel>& inputChannel);
+ explicit NativeInputChannel(const std::shared_ptr<InputChannel>& inputChannel);
~NativeInputChannel();
- inline sp<InputChannel> getInputChannel() { return mInputChannel; }
+ inline std::shared_ptr<InputChannel> getInputChannel() { return mInputChannel; }
void setDisposeCallback(InputChannelObjDisposeCallback callback, void* data);
void dispose(JNIEnv* env, jobject obj);
private:
- sp<InputChannel> mInputChannel;
+ std::shared_ptr<InputChannel> mInputChannel;
InputChannelObjDisposeCallback mDisposeCallback;
void* mDisposeData;
};
// ----------------------------------------------------------------------------
-NativeInputChannel::NativeInputChannel(const sp<InputChannel>& inputChannel) :
- mInputChannel(inputChannel), mDisposeCallback(nullptr) {
-}
+NativeInputChannel::NativeInputChannel(const std::shared_ptr<InputChannel>& inputChannel)
+ : mInputChannel(inputChannel), mDisposeCallback(nullptr) {}
NativeInputChannel::~NativeInputChannel() {
}
@@ -81,7 +80,7 @@
mDisposeCallback = nullptr;
mDisposeData = nullptr;
}
- mInputChannel.clear();
+ mInputChannel.reset();
}
// ----------------------------------------------------------------------------
@@ -92,7 +91,8 @@
return reinterpret_cast<NativeInputChannel*>(longPtr);
}
-sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env, jobject inputChannelObj) {
+std::shared_ptr<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env,
+ jobject inputChannelObj) {
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
return nativeInputChannel != nullptr ? nativeInputChannel->getInputChannel() : nullptr;
@@ -109,8 +109,8 @@
}
}
-static jlong android_view_InputChannel_createInputChannel(JNIEnv* env,
- sp<InputChannel> inputChannel) {
+static jlong android_view_InputChannel_createInputChannel(
+ JNIEnv* env, std::shared_ptr<InputChannel> inputChannel) {
std::unique_ptr<NativeInputChannel> nativeInputChannel =
std::make_unique<NativeInputChannel>(inputChannel);
@@ -122,8 +122,8 @@
ScopedUtfChars nameChars(env, nameObj);
std::string name = nameChars.c_str();
- sp<InputChannel> serverChannel;
- sp<InputChannel> clientChannel;
+ std::unique_ptr<InputChannel> serverChannel;
+ std::unique_ptr<InputChannel> clientChannel;
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
if (result) {
@@ -139,12 +139,12 @@
}
jlong* outArray = env->GetLongArrayElements(channelPair, 0);
- outArray[0] = android_view_InputChannel_createInputChannel(env, serverChannel);
+ outArray[0] = android_view_InputChannel_createInputChannel(env, std::move(serverChannel));
if (env->ExceptionCheck()) {
return nullptr;
}
- outArray[1] = android_view_InputChannel_createInputChannel(env, clientChannel);
+ outArray[1] = android_view_InputChannel_createInputChannel(env, std::move(clientChannel));
if (env->ExceptionCheck()) {
return nullptr;
}
@@ -180,7 +180,7 @@
if (parcel) {
bool isInitialized = parcel->readInt32();
if (isInitialized) {
- sp<InputChannel> inputChannel = new InputChannel();
+ std::shared_ptr<InputChannel> inputChannel = std::make_shared<InputChannel>();
inputChannel->readFromParcel(parcel);
NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel);
return reinterpret_cast<jlong>(nativeInputChannel);
@@ -227,13 +227,13 @@
return 0;
}
- sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
+ std::shared_ptr<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
if (inputChannel == nullptr) {
jniThrowRuntimeException(env, "NativeInputChannel has no corresponding InputChannel");
return 0;
}
- sp<InputChannel> dupInputChannel = inputChannel->dup();
+ std::shared_ptr<InputChannel> dupInputChannel = inputChannel->dup();
if (dupInputChannel == nullptr) {
std::string message = android::base::StringPrintf(
"Could not duplicate input channel %s", inputChannel->getName().c_str());
diff --git a/core/jni/android_view_InputChannel.h b/core/jni/android_view_InputChannel.h
index 2ba2dc0..8030c96 100644
--- a/core/jni/android_view_InputChannel.h
+++ b/core/jni/android_view_InputChannel.h
@@ -24,10 +24,11 @@
namespace android {
typedef void (*InputChannelObjDisposeCallback)(JNIEnv* env, jobject inputChannelObj,
- const sp<InputChannel>& inputChannel, void* data);
+ const std::shared_ptr<InputChannel>& inputChannel,
+ void* data);
-extern sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env,
- jobject inputChannelObj);
+extern std::shared_ptr<InputChannel> android_view_InputChannel_getInputChannel(
+ JNIEnv* env, jobject inputChannelObj);
/* Sets a callback that is invoked when the InputChannel DVM object is disposed (or finalized).
* This is used to automatically dispose of other native objects in the input dispatcher
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index cc94d6f..979a69a 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -55,9 +55,9 @@
class NativeInputEventReceiver : public LooperCallback {
public:
- NativeInputEventReceiver(JNIEnv* env,
- jobject receiverWeak, const sp<InputChannel>& inputChannel,
- const sp<MessageQueue>& messageQueue);
+ NativeInputEventReceiver(JNIEnv* env, jobject receiverWeak,
+ const std::shared_ptr<InputChannel>& inputChannel,
+ const sp<MessageQueue>& messageQueue);
status_t initialize();
void dispose();
@@ -91,13 +91,14 @@
virtual int handleEvent(int receiveFd, int events, void* data) override;
};
-
-NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
- jobject receiverWeak, const sp<InputChannel>& inputChannel,
- const sp<MessageQueue>& messageQueue) :
- mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
- mInputConsumer(inputChannel), mMessageQueue(messageQueue),
- mBatchedInputEventPending(false), mFdEvents(0) {
+NativeInputEventReceiver::NativeInputEventReceiver(
+ JNIEnv* env, jobject receiverWeak, const std::shared_ptr<InputChannel>& inputChannel,
+ const sp<MessageQueue>& messageQueue)
+ : mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
+ mInputConsumer(inputChannel),
+ mMessageQueue(messageQueue),
+ mBatchedInputEventPending(false),
+ mFdEvents(0) {
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName().c_str());
}
@@ -356,8 +357,8 @@
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
- sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
+ std::shared_ptr<InputChannel> inputChannel =
+ android_view_InputChannel_getInputChannel(env, inputChannelObj);
if (inputChannel == nullptr) {
jniThrowRuntimeException(env, "InputChannel is not initialized.");
return 0;
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 0a2b1d4..ac680f6 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -48,9 +48,9 @@
class NativeInputEventSender : public LooperCallback {
public:
- NativeInputEventSender(JNIEnv* env,
- jobject senderWeak, const sp<InputChannel>& inputChannel,
- const sp<MessageQueue>& messageQueue);
+ NativeInputEventSender(JNIEnv* env, jobject senderWeak,
+ const std::shared_ptr<InputChannel>& inputChannel,
+ const sp<MessageQueue>& messageQueue);
status_t initialize();
void dispose();
@@ -76,12 +76,12 @@
status_t receiveFinishedSignals(JNIEnv* env);
};
-
-NativeInputEventSender::NativeInputEventSender(JNIEnv* env,
- jobject senderWeak, const sp<InputChannel>& inputChannel,
- const sp<MessageQueue>& messageQueue) :
- mSenderWeakGlobal(env->NewGlobalRef(senderWeak)),
- mInputPublisher(inputChannel), mMessageQueue(messageQueue),
+NativeInputEventSender::NativeInputEventSender(JNIEnv* env, jobject senderWeak,
+ const std::shared_ptr<InputChannel>& inputChannel,
+ const sp<MessageQueue>& messageQueue)
+ : mSenderWeakGlobal(env->NewGlobalRef(senderWeak)),
+ mInputPublisher(inputChannel),
+ mMessageQueue(messageQueue),
mNextPublishedSeq(1) {
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Initializing input event sender.", getInputChannelName().c_str());
@@ -144,9 +144,8 @@
event->getAction(), event->getActionButton(),
event->getFlags(), event->getEdgeFlags(),
event->getMetaState(), event->getButtonState(),
- event->getClassification(), event->getXScale(),
- event->getYScale(), event->getXOffset(),
- event->getYOffset(), event->getXPrecision(),
+ event->getClassification(),
+ event->getTransform(), event->getXPrecision(),
event->getYPrecision(),
event->getRawXCursorPosition(),
event->getRawYCursorPosition(),
@@ -249,8 +248,8 @@
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject senderWeak,
jobject inputChannelObj, jobject messageQueueObj) {
- sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
+ std::shared_ptr<InputChannel> inputChannel =
+ android_view_InputChannel_getInputChannel(env, inputChannelObj);
if (inputChannel == NULL) {
jniThrowRuntimeException(env, "InputChannel is not initialized.");
return 0;
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 15a9110..2e396f2 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -369,13 +369,14 @@
env->DeleteLocalRef(pointerCoordsObj);
}
+ ui::Transform transform;
+ transform.set(xOffset, yOffset);
event->initialize(InputEvent::nextId(), deviceId, source, displayId, INVALID_HMAC, action, 0,
flags, edgeFlags, metaState, buttonState,
- static_cast<MotionClassification>(classification), 1 /*xScale*/, 1 /*yScale*/,
- xOffset, yOffset, xPrecision, yPrecision,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- downTimeNanos, eventTimeNanos, pointerCount, pointerProperties,
- rawPointerCoords);
+ static_cast<MotionClassification>(classification), transform, xPrecision,
+ yPrecision, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, downTimeNanos, eventTimeNanos,
+ pointerCount, pointerProperties, rawPointerCoords);
return reinterpret_cast<jlong>(event.release());
}
@@ -569,9 +570,9 @@
jlong nativePtr, jobject matrixObj) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- float m[9];
- AMatrix_getContents(env, matrixObj, m);
- event->transform(m);
+ std::array<float, 9> matrix;
+ AMatrix_getContents(env, matrixObj, matrix.data());
+ event->transform(matrix);
}
// ----------------- @CriticalNative ------------------------------
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/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 9bbe0ca..ac143ac 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -508,7 +508,7 @@
optional SettingProto job_scheduler_constants = 66 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto job_scheduler_quota_controller_constants = 149 [ (android.privacy).dest = DEST_AUTOMATIC ];
- optional SettingProto job_scheduler_time_controller_constants = 150 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ reserved 150; // job_scheduler_time_controller_constants
optional SettingProto keep_profile_in_background = 67 [ (android.privacy).dest = DEST_AUTOMATIC ];
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index f2f20e3a..76b7fc0 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -324,7 +324,7 @@
// ready now.
reserved 1; // skip_not_ready_jobs
// Whether or not TimeController will use a non-wakeup alarm for delay constraints.
- optional bool use_non_wakeup_alarm_for_delay = 2;
+ reserved 2; // use_non_wakeup_alarm_for_delay
}
optional TimeController time_controller = 25;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ef50fc8..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" />
@@ -5034,6 +5035,11 @@
<permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"
android:protectionLevel="signature|appPredictor" />
+ <!-- @hide @TestApi Allows apps to reset the state of {@link com.android.server.am.AppErrors}.
+ <p>CTS tests will use UiAutomation.adoptShellPermissionIdentity() to gain access. -->
+ <permission android:name="android.permission.RESET_APP_ERRORS"
+ android:protectionLevel="signature" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
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/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 12275dc..4d74cf7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2128,7 +2128,7 @@
<!-- On the unlock pattern screen, shown at the top of the unlock screen to tell the user what to do. Below this text is the place for theu ser to draw the pattern. -->
<string name="lockscreen_pattern_instructions">Draw pattern to unlock</string>
<!-- Button at the bottom of the unlock screen to make an emergency call or access other emergency assistance functions. -->
- <string name="lockscreen_emergency_call">Emergency</string>
+ <string name="lockscreen_emergency_call">Emergency call</string>
<!-- Button at the bottom of the unlock screen that lets the user return to a call -->
<string name="lockscreen_return_to_call">Return to call</string>
<!-- Shown to confirm that the user entered their lock pattern correctly. -->
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/core/tests/coretests/src/android/text/format/DateFormatTest.java b/core/tests/coretests/src/android/text/format/DateFormatTest.java
index fa1d56f..a3434e8 100644
--- a/core/tests/coretests/src/android/text/format/DateFormatTest.java
+++ b/core/tests/coretests/src/android/text/format/DateFormatTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.icu.text.DateFormatSymbols;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -60,6 +61,15 @@
}
@Test
+ public void testgetIcuDateFormatSymbols() {
+ DateFormatSymbols dfs = DateFormat.getIcuDateFormatSymbols(Locale.US);
+ assertEquals("AM", dfs.getAmPmStrings()[0]);
+ assertEquals("PM", dfs.getAmPmStrings()[1]);
+ assertEquals("a", dfs.getAmpmNarrowStrings()[0]);
+ assertEquals("p", dfs.getAmpmNarrowStrings()[1]);
+ }
+
+ @Test
public void testGetDateFormatOrder() {
// lv and fa use differing orders depending on whether you're using numeric or
// textual months.
diff --git a/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
new file mode 100644
index 0000000..0f17d27
--- /dev/null
+++ b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
@@ -0,0 +1,671 @@
+/*
+ * Copyright (C) 2013 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.text.format;
+
+import static android.icu.util.TimeZone.GMT_ZONE;
+import static android.icu.util.ULocale.ENGLISH;
+import static android.text.format.DateIntervalFormat.formatDateRange;
+import static android.text.format.DateUtils.FORMAT_12HOUR;
+import static android.text.format.DateUtils.FORMAT_24HOUR;
+import static android.text.format.DateUtils.FORMAT_ABBREV_ALL;
+import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
+import static android.text.format.DateUtils.FORMAT_ABBREV_TIME;
+import static android.text.format.DateUtils.FORMAT_ABBREV_WEEKDAY;
+import static android.text.format.DateUtils.FORMAT_NO_MONTH_DAY;
+import static android.text.format.DateUtils.FORMAT_NO_YEAR;
+import static android.text.format.DateUtils.FORMAT_NUMERIC_DATE;
+import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
+import static android.text.format.DateUtils.FORMAT_SHOW_TIME;
+import static android.text.format.DateUtils.FORMAT_SHOW_WEEKDAY;
+import static android.text.format.DateUtils.FORMAT_SHOW_YEAR;
+import static android.text.format.DateUtils.FORMAT_UTC;
+
+import static org.junit.Assert.assertEquals;
+
+import android.icu.util.Calendar;
+import android.icu.util.TimeZone;
+import android.icu.util.ULocale;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.BiFunction;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DateIntervalFormatTest {
+ private static final long MINUTE = 60 * 1000;
+ private static final long HOUR = 60 * MINUTE;
+ private static final long DAY = 24 * HOUR;
+ private static final long MONTH = 31 * DAY;
+ private static final long YEAR = 12 * MONTH;
+
+ // These are the old CTS tests for DateIntervalFormat.formatDateRange.
+ @Test
+ public void test_formatDateInterval() throws Exception {
+ TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+
+ Calendar c = Calendar.getInstance(tz, ULocale.US);
+ c.set(Calendar.MONTH, Calendar.JANUARY);
+ c.set(Calendar.DAY_OF_MONTH, 19);
+ c.set(Calendar.HOUR_OF_DAY, 3);
+ c.set(Calendar.MINUTE, 30);
+ c.set(Calendar.SECOND, 15);
+ c.set(Calendar.MILLISECOND, 0);
+ long timeWithCurrentYear = c.getTimeInMillis();
+
+ c.set(Calendar.YEAR, 2009);
+ long fixedTime = c.getTimeInMillis();
+
+ c.set(Calendar.MINUTE, 0);
+ c.set(Calendar.SECOND, 0);
+ long onTheHour = c.getTimeInMillis();
+
+ long noonDuration = (8 * 60 + 30) * 60 * 1000 - 15 * 1000;
+ long midnightDuration = (3 * 60 + 30) * 60 * 1000 + 15 * 1000;
+
+ ULocale de_DE = new ULocale("de", "DE");
+ ULocale en_US = new ULocale("en", "US");
+ ULocale es_ES = new ULocale("es", "ES");
+ ULocale es_US = new ULocale("es", "US");
+
+ assertEquals("Monday",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_WEEKDAY));
+ assertEquals("January 19",
+ formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR,
+ FORMAT_SHOW_DATE));
+ assertEquals("3:30 AM", formatDateRange(en_US, tz, fixedTime, fixedTime, FORMAT_SHOW_TIME));
+ assertEquals("January 19, 2009",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_YEAR));
+ assertEquals("January 19",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR, FORMAT_NO_YEAR));
+ assertEquals("January",
+ formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR,
+ FORMAT_NO_MONTH_DAY));
+ assertEquals("3:30 AM",
+ formatDateRange(en_US, tz, fixedTime, fixedTime, FORMAT_12HOUR | FORMAT_SHOW_TIME));
+ assertEquals("03:30",
+ formatDateRange(en_US, tz, fixedTime, fixedTime, FORMAT_24HOUR | FORMAT_SHOW_TIME));
+ assertEquals("3:30 AM", formatDateRange(en_US, tz, fixedTime, fixedTime,
+ FORMAT_12HOUR /*| FORMAT_CAP_AMPM*/ | FORMAT_SHOW_TIME));
+ assertEquals("12:00 PM",
+ formatDateRange(en_US, tz, fixedTime + noonDuration, fixedTime + noonDuration,
+ FORMAT_12HOUR | FORMAT_SHOW_TIME));
+ assertEquals("12:00 PM",
+ formatDateRange(en_US, tz, fixedTime + noonDuration, fixedTime + noonDuration,
+ FORMAT_12HOUR | FORMAT_SHOW_TIME /*| FORMAT_CAP_NOON*/));
+ assertEquals("12:00 PM",
+ formatDateRange(en_US, tz, fixedTime + noonDuration, fixedTime + noonDuration,
+ FORMAT_12HOUR /*| FORMAT_NO_NOON*/ | FORMAT_SHOW_TIME));
+ assertEquals("12:00 AM", formatDateRange(en_US, tz, fixedTime - midnightDuration,
+ fixedTime - midnightDuration,
+ FORMAT_12HOUR | FORMAT_SHOW_TIME /*| FORMAT_NO_MIDNIGHT*/));
+ assertEquals("3:30 AM",
+ formatDateRange(en_US, tz, fixedTime, fixedTime, FORMAT_SHOW_TIME | FORMAT_UTC));
+ assertEquals("3 AM", formatDateRange(en_US, tz, onTheHour, onTheHour,
+ FORMAT_SHOW_TIME | FORMAT_ABBREV_TIME));
+ assertEquals("Mon", formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR,
+ FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_WEEKDAY));
+ assertEquals("Jan 19",
+ formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH));
+ assertEquals("Jan 19",
+ formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+
+ assertEquals("1/19/2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * HOUR,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("1/19/2009 – 1/22/2009",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("1/19/2009 – 4/22/2009",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("1/19/2009 – 2/9/2012",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+
+ assertEquals("19.1.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + HOUR,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19.–22.01.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19.01. – 22.04.2009",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19.01.2009 – 09.02.2012",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+
+ assertEquals("19/1/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + HOUR,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19/1/2009–22/1/2009",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19/1/2009–22/4/2009",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19/1/2009–9/2/2012",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+
+ assertEquals("19/1/2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + HOUR,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19/1/2009–22/1/2009",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19/1/2009–22/4/2009",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19/1/2009–9/2/2012",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+
+ // These are some random other test cases I came up with.
+
+ assertEquals("January 19 – 22, 2009",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, 0));
+ assertEquals("Jan 19 – 22, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("Mon, Jan 19 – Thu, Jan 22, 2009",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("Monday, January 19 – Thursday, January 22, 2009",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
+
+ assertEquals("January 19 – April 22, 2009",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, 0));
+ assertEquals("Jan 19 – Apr 22, 2009",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("Mon, Jan 19 – Wed, Apr 22, 2009",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("January – April 2009",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
+
+ assertEquals("Jan 19, 2009 – Feb 9, 2012",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("Jan 2009 – Feb 2012",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
+ assertEquals("January 19, 2009 – February 9, 2012",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+ assertEquals("Monday, January 19, 2009 – Thursday, February 9, 2012",
+ formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
+
+ // The same tests but for de_DE.
+
+ assertEquals("19.–22. Januar 2009",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, 0));
+ assertEquals("19.–22. Jan. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("Mo., 19. – Do., 22. Jan. 2009",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("Montag, 19. – Donnerstag, 22. Januar 2009",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
+
+ assertEquals("19. Januar – 22. April 2009",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, 0));
+ assertEquals("19. Jan. – 22. Apr. 2009",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("Mo., 19. Jan. – Mi., 22. Apr. 2009",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("Januar–April 2009",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
+
+ assertEquals("19. Jan. 2009 – 9. Feb. 2012",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("Jan. 2009 – Feb. 2012",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
+ assertEquals("19. Januar 2009 – 9. Februar 2012",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+ assertEquals("Montag, 19. Januar 2009 – Donnerstag, 9. Februar 2012",
+ formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
+
+ // The same tests but for es_US.
+
+ assertEquals("19–22 de enero de 2009",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, 0));
+ assertEquals("19–22 de ene. de 2009",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("lun., 19 de ene. – jue., 22 de ene. de 2009",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
+
+ assertEquals("19 de enero–22 de abril de 2009",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, 0));
+ assertEquals("19 de ene. – 22 de abr. 2009",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("lun., 19 de ene. – mié., 22 de abr. de 2009",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("enero–abril de 2009",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
+
+ assertEquals("19 de ene. de 2009 – 9 de feb. de 2012",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("ene. de 2009 – feb. de 2012",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
+ assertEquals("19 de enero de 2009–9 de febrero de 2012",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+ assertEquals("lunes, 19 de enero de 2009–jueves, 9 de febrero de 2012",
+ formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
+
+ // The same tests but for es_ES.
+
+ assertEquals("19–22 de enero de 2009",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, 0));
+ assertEquals("19–22 ene. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("lun., 19 ene. – jue., 22 ene. 2009",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY,
+ FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
+
+ assertEquals("19 de enero–22 de abril de 2009",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, 0));
+ assertEquals("19 ene. – 22 abr. 2009",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("lun., 19 ene. – mié., 22 abr. 2009",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH,
+ FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("enero–abril de 2009",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
+
+ assertEquals("19 ene. 2009 – 9 feb. 2012",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("ene. 2009 – feb. 2012",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR,
+ FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
+ assertEquals("19 de enero de 2009–9 de febrero de 2012",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+ assertEquals("lunes, 19 de enero de 2009–jueves, 9 de febrero de 2012",
+ formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
+ }
+
+ // http://b/8862241 - we should be able to format dates past 2038.
+ // See also http://code.google.com/p/android/issues/detail?id=13050.
+ @Test
+ public void test8862241() throws Exception {
+ ULocale l = ULocale.US;
+ TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+ Calendar c = Calendar.getInstance(tz, l);
+ c.clear();
+ c.set(2042, Calendar.JANUARY, 19, 3, 30);
+ long jan_19_2042 = c.getTimeInMillis();
+ c.set(2046, Calendar.OCTOBER, 4, 3, 30);
+ long oct_4_2046 = c.getTimeInMillis();
+ int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL;
+ assertEquals("Jan 19, 2042 – Oct 4, 2046",
+ formatDateRange(l, tz, jan_19_2042, oct_4_2046, flags));
+ }
+
+ // http://b/10089890 - we should take the given time zone into account.
+ @Test
+ public void test10089890() throws Exception {
+ ULocale l = ULocale.US;
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+ TimeZone pacific = TimeZone.getTimeZone("America/Los_Angeles");
+ int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL | FORMAT_SHOW_TIME | FORMAT_24HOUR;
+
+ // The Unix epoch is UTC, so 0 is 1970-01-01T00:00Z...
+ assertEquals("Jan 1, 1970, 00:00 – Jan 2, 1970, 00:00",
+ formatDateRange(l, utc, 0, DAY + 1, flags));
+ // But MTV is hours behind, so 0 was still the afternoon of the previous day...
+ assertEquals("Dec 31, 1969, 16:00 – Jan 1, 1970, 16:00",
+ formatDateRange(l, pacific, 0, DAY, flags));
+ }
+
+ // http://b/10318326 - we can drop the minutes in a 12-hour time if they're zero,
+ // but not if we're using the 24-hour clock. That is: "4 PM" is reasonable, "16" is not.
+ @Test
+ public void test10318326() throws Exception {
+ long midnight = 0;
+ long teaTime = 16 * HOUR;
+
+ int time12 = FORMAT_12HOUR | FORMAT_SHOW_TIME;
+ int time24 = FORMAT_24HOUR | FORMAT_SHOW_TIME;
+ int abbr12 = time12 | FORMAT_ABBREV_ALL;
+ int abbr24 = time24 | FORMAT_ABBREV_ALL;
+
+ ULocale l = ULocale.US;
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+
+ // Full length on-the-hour times.
+ assertEquals("00:00", formatDateRange(l, utc, midnight, midnight, time24));
+ assertEquals("12:00 AM", formatDateRange(l, utc, midnight, midnight, time12));
+ assertEquals("16:00", formatDateRange(l, utc, teaTime, teaTime, time24));
+ assertEquals("4:00 PM", formatDateRange(l, utc, teaTime, teaTime, time12));
+
+ // Abbreviated on-the-hour times.
+ assertEquals("00:00", formatDateRange(l, utc, midnight, midnight, abbr24));
+ assertEquals("12 AM", formatDateRange(l, utc, midnight, midnight, abbr12));
+ assertEquals("16:00", formatDateRange(l, utc, teaTime, teaTime, abbr24));
+ assertEquals("4 PM", formatDateRange(l, utc, teaTime, teaTime, abbr12));
+
+ // Abbreviated on-the-hour ranges.
+ assertEquals("00:00 – 16:00", formatDateRange(l, utc, midnight, teaTime, abbr24));
+ assertEquals("12 AM – 4 PM", formatDateRange(l, utc, midnight, teaTime, abbr12));
+
+ // Abbreviated mixed ranges.
+ assertEquals("00:00 – 16:01", formatDateRange(l, utc, midnight, teaTime + MINUTE, abbr24));
+ assertEquals("12:00 AM – 4:01 PM",
+ formatDateRange(l, utc, midnight, teaTime + MINUTE, abbr12));
+ }
+
+ // http://b/10560853 - when the time is not displayed, an end time 0 ms into the next day is
+ // considered to belong to the previous day.
+ @Test
+ public void test10560853_when_time_not_displayed() throws Exception {
+ ULocale l = ULocale.US;
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+
+ long midnight = 0;
+ long midnightNext = 1 * DAY;
+
+ int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY;
+
+ // An all-day event runs until 0 milliseconds into the next day, but is formatted as if it's
+ // just the first day.
+ assertEquals("Thursday, January 1, 1970",
+ formatDateRange(l, utc, midnight, midnightNext, flags));
+
+ // Run one millisecond over, though, and you're into the next day.
+ long nextMorning = 1 * DAY + 1;
+ assertEquals("Thursday, January 1 – Friday, January 2, 1970",
+ formatDateRange(l, utc, midnight, nextMorning, flags));
+
+ // But the same reasoning applies for that day.
+ long nextMidnight = 2 * DAY;
+ assertEquals("Thursday, January 1 – Friday, January 2, 1970",
+ formatDateRange(l, utc, midnight, nextMidnight, flags));
+ }
+
+ // http://b/10560853 - when the start and end times are otherwise on the same day,
+ // an end time 0 ms into the next day is considered to belong to the previous day.
+ @Test
+ public void test10560853_for_single_day_events() throws Exception {
+ ULocale l = ULocale.US;
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+
+ int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE;
+
+ assertEquals("January 1, 1970, 22:00 – 00:00",
+ formatDateRange(l, utc, 22 * HOUR, 24 * HOUR, flags));
+ assertEquals("January 1, 1970, 22:00 – January 2, 1970, 00:30",
+ formatDateRange(l, utc, 22 * HOUR, 24 * HOUR + 30 * MINUTE, flags));
+ }
+
+ // The fix for http://b/10560853 didn't work except for the day around the epoch, which was
+ // all the unit test checked!
+ @Test
+ public void test_single_day_events_later_than_epoch() throws Exception {
+ ULocale l = ULocale.US;
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+
+ int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE;
+
+ Calendar c = Calendar.getInstance(utc, l);
+ c.clear();
+ c.set(1980, Calendar.JANUARY, 1, 0, 0);
+ long jan_1_1980 = c.getTimeInMillis();
+ assertEquals("January 1, 1980, 22:00 – 00:00",
+ formatDateRange(l, utc, jan_1_1980 + 22 * HOUR, jan_1_1980 + 24 * HOUR, flags));
+ assertEquals("January 1, 1980, 22:00 – January 2, 1980, 00:30",
+ formatDateRange(l, utc, jan_1_1980 + 22 * HOUR,
+ jan_1_1980 + 24 * HOUR + 30 * MINUTE, flags));
+ }
+
+ // The fix for http://b/10560853 didn't work except for UTC, which was
+ // all the unit test checked!
+ @Test
+ public void test_single_day_events_not_in_UTC() throws Exception {
+ ULocale l = ULocale.US;
+ TimeZone pacific = TimeZone.getTimeZone("America/Los_Angeles");
+
+ int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE;
+
+ Calendar c = Calendar.getInstance(pacific, l);
+ c.clear();
+ c.set(1980, Calendar.JANUARY, 1, 0, 0);
+ long jan_1_1980 = c.getTimeInMillis();
+ assertEquals("January 1, 1980, 22:00 – 00:00",
+ formatDateRange(l, pacific, jan_1_1980 + 22 * HOUR, jan_1_1980 + 24 * HOUR, flags));
+
+ c.set(1980, Calendar.JULY, 1, 0, 0);
+ long jul_1_1980 = c.getTimeInMillis();
+ assertEquals("July 1, 1980, 22:00 – 00:00",
+ formatDateRange(l, pacific, jul_1_1980 + 22 * HOUR, jul_1_1980 + 24 * HOUR, flags));
+ }
+
+ // http://b/10209343 - even if the caller didn't explicitly ask us to include the year,
+ // we should do so for years other than the current year.
+ @Test
+ public void test10209343_when_not_this_year() {
+ ULocale l = ULocale.US;
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+
+ int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_TIME | FORMAT_24HOUR;
+
+ assertEquals("Thursday, January 1, 1970, 00:00", formatDateRange(l, utc, 0L, 0L, flags));
+
+ long t1833 = ((long) Integer.MIN_VALUE + Integer.MIN_VALUE) * 1000L;
+ assertEquals("Sunday, November 24, 1833, 17:31",
+ formatDateRange(l, utc, t1833, t1833, flags));
+
+ long t1901 = Integer.MIN_VALUE * 1000L;
+ assertEquals("Friday, December 13, 1901, 20:45",
+ formatDateRange(l, utc, t1901, t1901, flags));
+
+ long t2038 = Integer.MAX_VALUE * 1000L;
+ assertEquals("Tuesday, January 19, 2038, 03:14",
+ formatDateRange(l, utc, t2038, t2038, flags));
+
+ long t2106 = (2L + Integer.MAX_VALUE + Integer.MAX_VALUE) * 1000L;
+ assertEquals("Sunday, February 7, 2106, 06:28",
+ formatDateRange(l, utc, t2106, t2106, flags));
+ }
+
+ // http://b/10209343 - for the current year, we should honor the FORMAT_SHOW_YEAR flags.
+ @Test
+ public void test10209343_when_this_year() {
+ // Construct a date in the current year (whenever the test happens to be run).
+ ULocale l = ULocale.US;
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+ Calendar c = Calendar.getInstance(utc, l);
+ c.set(Calendar.MONTH, Calendar.FEBRUARY);
+ c.set(Calendar.DAY_OF_MONTH, 10);
+ c.set(Calendar.HOUR_OF_DAY, 0);
+ long thisYear = c.getTimeInMillis();
+
+ // You don't get the year if it's this year...
+ assertEquals("February 10", formatDateRange(l, utc, thisYear, thisYear, FORMAT_SHOW_DATE));
+
+ // ...unless you explicitly ask for it.
+ assertEquals(String.format("February 10, %d", c.get(Calendar.YEAR)),
+ formatDateRange(l, utc, thisYear, thisYear, FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR));
+
+ // ...or it's not actually this year...
+ Calendar c2 = (Calendar) c.clone();
+ c2.set(Calendar.YEAR, 1980);
+ long oldYear = c2.getTimeInMillis();
+ assertEquals("February 10, 1980",
+ formatDateRange(l, utc, oldYear, oldYear, FORMAT_SHOW_DATE));
+
+ // (But you can disable that!)
+ assertEquals("February 10",
+ formatDateRange(l, utc, oldYear, oldYear, FORMAT_SHOW_DATE | FORMAT_NO_YEAR));
+
+ // ...or the start and end years aren't the same...
+ assertEquals(String.format("February 10, 1980 – February 10, %d", c.get(Calendar.YEAR)),
+ formatDateRange(l, utc, oldYear, thisYear, FORMAT_SHOW_DATE));
+
+ // (And you can't avoid that --- icu4c steps in and overrides you.)
+ assertEquals(String.format("February 10, 1980 – February 10, %d", c.get(Calendar.YEAR)),
+ formatDateRange(l, utc, oldYear, thisYear, FORMAT_SHOW_DATE | FORMAT_NO_YEAR));
+ }
+
+ // http://b/8467515 - yet another y2k38 bug report.
+ @Test
+ public void test8467515() throws Exception {
+ ULocale l = ULocale.US;
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+ int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_YEAR | FORMAT_ABBREV_MONTH
+ | FORMAT_ABBREV_WEEKDAY;
+ long t;
+
+ Calendar calendar = Calendar.getInstance(utc, l);
+ calendar.clear();
+
+ calendar.set(2038, Calendar.JANUARY, 19, 12, 0, 0);
+ t = calendar.getTimeInMillis();
+ assertEquals("Tue, Jan 19, 2038", formatDateRange(l, utc, t, t, flags));
+
+ calendar.set(1900, Calendar.JANUARY, 1, 0, 0, 0);
+ t = calendar.getTimeInMillis();
+ assertEquals("Mon, Jan 1, 1900", formatDateRange(l, utc, t, t, flags));
+ }
+
+ // http://b/12004664
+ @Test
+ public void test12004664() throws Exception {
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+ Calendar c = Calendar.getInstance(utc, ULocale.US);
+ c.clear();
+ c.set(Calendar.YEAR, 1980);
+ c.set(Calendar.MONTH, Calendar.FEBRUARY);
+ c.set(Calendar.DAY_OF_MONTH, 10);
+ c.set(Calendar.HOUR_OF_DAY, 0);
+ long thisYear = c.getTimeInMillis();
+
+ int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_YEAR;
+ assertEquals("Sunday, February 10, 1980",
+ formatDateRange(new ULocale("en", "US"), utc, thisYear, thisYear, flags));
+
+ // If we supported non-Gregorian calendars, this is what that we'd expect for these
+ // ULocales.
+ // This is really the correct behavior, but since java.util.Calendar currently only supports
+ // the Gregorian calendar, we want to deliberately force icu4c to agree, otherwise we'd have
+ // a mix of calendars throughout an app's UI depending on whether Java or native code
+ // formatted
+ // the date.
+ // assertEquals("یکشنبه ۲۱ بهمن ۱۳۵۸ ه.ش.", formatDateRange(new ULocale("fa"), utc,
+ // thisYear, thisYear, flags));
+ // assertEquals("AP ۱۳۵۸ سلواغه ۲۱, یکشنبه", formatDateRange(new ULocale("ps"), utc,
+ // thisYear, thisYear, flags));
+ // assertEquals("วันอาทิตย์ 10 กุมภาพันธ์ 2523", formatDateRange(new ULocale("th"), utc,
+ // thisYear, thisYear, flags));
+
+ // For now, here are the localized Gregorian strings instead...
+ assertEquals("یکشنبه ۱۰ فوریهٔ ۱۹۸۰",
+ formatDateRange(new ULocale("fa"), utc, thisYear, thisYear, flags));
+ assertEquals("يونۍ د ۱۹۸۰ د فبروري ۱۰",
+ formatDateRange(new ULocale("ps"), utc, thisYear, thisYear, flags));
+ assertEquals("วันอาทิตย์ที่ 10 กุมภาพันธ์ ค.ศ. 1980",
+ formatDateRange(new ULocale("th"), utc, thisYear, thisYear, flags));
+ }
+
+ // http://b/13234532
+ @Test
+ public void test13234532() throws Exception {
+ ULocale l = ULocale.US;
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+
+ int flags = FORMAT_SHOW_TIME | FORMAT_ABBREV_ALL | FORMAT_12HOUR;
+
+ assertEquals("10 – 11 AM", formatDateRange(l, utc, 10 * HOUR, 11 * HOUR, flags));
+ assertEquals("11 AM – 1 PM", formatDateRange(l, utc, 11 * HOUR, 13 * HOUR, flags));
+ assertEquals("2 – 3 PM", formatDateRange(l, utc, 14 * HOUR, 15 * HOUR, flags));
+ }
+
+ // http://b/20708022
+ @Test
+ public void testEndOfDayOnLastDayOfMonth() throws Exception {
+ final ULocale locale = new ULocale("en");
+ final TimeZone timeZone = TimeZone.getTimeZone("UTC");
+
+ assertEquals("11:00 PM – 12:00 AM", formatDateRange(locale, timeZone,
+ 1430434800000L, 1430438400000L, FORMAT_SHOW_TIME));
+ }
+
+ // http://b/68847519
+ @Test
+ public void testEndAtMidnight_dateAndTime() {
+ BiFunction<Long, Long, String> fmt = (from, to) -> formatDateRange(
+ ENGLISH, GMT_ZONE, from, to, FORMAT_SHOW_DATE | FORMAT_SHOW_TIME | FORMAT_24HOUR);
+ // If we're showing times and the end-point is midnight the following day, we want the
+ // behaviour of suppressing the date for the end...
+ assertEquals("February 27, 2007, 04:00 – 00:00", fmt.apply(1172548800000L, 1172620800000L));
+ // ...unless the start-point is also midnight, in which case we need dates to disambiguate.
+ assertEquals("February 27, 2007, 00:00 – February 28, 2007, 00:00",
+ fmt.apply(1172534400000L, 1172620800000L));
+ // We want to show the date if the end-point is a millisecond after midnight the following
+ // day, or if it is exactly midnight the day after that.
+ assertEquals("February 27, 2007, 04:00 – February 28, 2007, 00:00",
+ fmt.apply(1172548800000L, 1172620800001L));
+ assertEquals("February 27, 2007, 04:00 – March 1, 2007, 00:00",
+ fmt.apply(1172548800000L, 1172707200000L));
+ // We want to show the date if the start-point is anything less than a minute after
+ // midnight,
+ // since that gets displayed as midnight...
+ assertEquals("February 27, 2007, 00:00 – February 28, 2007, 00:00",
+ fmt.apply(1172534459999L, 1172620800000L));
+ // ...but not if it is exactly one minute after midnight.
+ assertEquals("February 27, 2007, 00:01 – 00:00", fmt.apply(1172534460000L, 1172620800000L));
+ }
+
+ // http://b/68847519
+ @Test
+ public void testEndAtMidnight_dateOnly() {
+ BiFunction<Long, Long, String> fmt = (from, to) -> formatDateRange(
+ ENGLISH, GMT_ZONE, from, to, FORMAT_SHOW_DATE);
+ // If we're only showing dates and the end-point is midnight of any day, we want the
+ // behaviour of showing an end date one earlier. So if the end-point is March 2, 2007 00:00,
+ // show March 1, 2007 instead (whether the start-point is midnight or not).
+ assertEquals("February 27 – March 1, 2007", fmt.apply(1172534400000L, 1172793600000L));
+ assertEquals("February 27 – March 1, 2007", fmt.apply(1172548800000L, 1172793600000L));
+ // We want to show the true date if the end-point is a millisecond after midnight.
+ assertEquals("February 27 – March 2, 2007", fmt.apply(1172534400000L, 1172793600001L));
+
+ // 2006-02-27 00:00:00.000 GMT - 2007-03-02 00:00:00.000 GMT
+ assertEquals("February 27, 2006 – March 1, 2007",
+ fmt.apply(1140998400000L, 1172793600000L));
+
+ // Spans a leap year's Feb 29th.
+ assertEquals("February 27 – March 1, 2004", fmt.apply(1077840000000L, 1078185600000L));
+ }
+}
diff --git a/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java b/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java
index d9ba8fb..4b3b573 100644
--- a/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java
+++ b/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java
@@ -16,11 +16,11 @@
package android.text.format;
-import static android.text.format.DateUtilsBridge.FORMAT_ABBREV_ALL;
-import static android.text.format.DateUtilsBridge.FORMAT_ABBREV_RELATIVE;
-import static android.text.format.DateUtilsBridge.FORMAT_NO_YEAR;
-import static android.text.format.DateUtilsBridge.FORMAT_NUMERIC_DATE;
-import static android.text.format.DateUtilsBridge.FORMAT_SHOW_YEAR;
+import static android.text.format.DateUtils.FORMAT_ABBREV_ALL;
+import static android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE;
+import static android.text.format.DateUtils.FORMAT_NO_YEAR;
+import static android.text.format.DateUtils.FORMAT_NUMERIC_DATE;
+import static android.text.format.DateUtils.FORMAT_SHOW_YEAR;
import static android.text.format.RelativeDateTimeFormatter.DAY_IN_MILLIS;
import static android.text.format.RelativeDateTimeFormatter.HOUR_IN_MILLIS;
import static android.text.format.RelativeDateTimeFormatter.MINUTE_IN_MILLIS;
diff --git a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
index 02ffc00..93de03a 100644
--- a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
@@ -264,6 +264,25 @@
InputConnection.GET_TEXT_WITH_STYLES)));
}
+ @Test
+ public void surroundingTextRetrieval_writeToParcel_noException() {
+ StringBuilder sb = new StringBuilder("abcdefg");
+ Parcel parcel = Parcel.obtain();
+ EditorInfo editorInfo = new EditorInfo();
+ editorInfo.initialSelStart = 2;
+ editorInfo.initialSelEnd = 5;
+ editorInfo.inputType = EditorInfo.TYPE_CLASS_TEXT;
+
+ editorInfo.setInitialSurroundingText(sb);
+ sb.setLength(0);
+ editorInfo.writeToParcel(parcel, 0);
+
+ try {
+ editorInfo.getInitialTextBeforeCursor(60, 1);
+ fail("Test shouldn't have exception");
+ } catch (AssertionError e) { }
+ }
+
private static void assertExpectedTextLength(EditorInfo editorInfo,
@Nullable Integer expectBeforeCursorLength, @Nullable Integer expectSelectionLength,
@Nullable Integer expectAfterCursorLength) {
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/etc/preinstalled-packages-platform-overlays.xml b/data/etc/preinstalled-packages-platform-overlays.xml
index 84c1897a2..83323be 100644
--- a/data/etc/preinstalled-packages-platform-overlays.xml
+++ b/data/etc/preinstalled-packages-platform-overlays.xml
@@ -19,250 +19,188 @@
<config>
<install-in-user-type package="com.android.internal.display.cutout.emulation.corner">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.internal.display.cutout.emulation.double">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.internal.display.cutout.emulation.hole">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.internal.display.cutout.emulation.tall">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.internal.display.cutout.emulation.waterfall">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
- </install-in-user-type>
- <install-in-user-type package="com.android.internal.systemui.navbar.twobutton">
- <install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
- </install-in-user-type>
- <install-in-user-type package="com.android.internal.systemui.navbar.threebutton">
- <install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.internal.systemui.navbar.gestural">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.internal.systemui.navbar.gestural_extra_wide_back">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.internal.systemui.navbar.gestural_narrow_back">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.internal.systemui.navbar.gestural_wide_back">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.internal.systemui.navbar.threebutton">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.internal.systemui.navbar.twobutton">
+ <install-in user-type="FULL" />
</install-in-user-type>
<install-in-user-type package="com.android.internal.systemui.onehanded.gestural">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.color.amethyst">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.color.aquamarine">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.color.black">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.color.carbon">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.color.cinnamon">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.color.green">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.color.ocean">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.color.orchid">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.color.palette">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.color.purple">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.color.sand">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.color.space">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.color.tangerine">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.font.notoserifsource">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.circular.android">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.circular.launcher">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.circular.settings">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.circular.systemui">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.circular.themepicker">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.filled.android">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.filled.launcher">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.filled.settings">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.filled.systemui">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.filled.themepicker">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.kai.android">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.kai.launcher">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.kai.settings">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.kai.systemui">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.kai.themepicker">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.rounded.android">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.rounded.launcher">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.rounded.settings">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.rounded.systemui">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.rounded.themepicker">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.sam.android">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.sam.launcher">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.sam.settings">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.sam.systemui">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.sam.themepicker">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.victor.android">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.victor.launcher">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.victor.settings">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.victor.systemui">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon_pack.victor.themepicker">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon.pebble">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon.roundedrect">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon.squircle">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon.taperedrect">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon.teardrop">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.icon.vessel">
<install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
</install-in-user-type>
</config>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 6798c0a..f834ce7 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -143,6 +143,9 @@
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
<permission name="android.permission.PACKAGE_USAGE_STATS" />
<permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
+
+ <!-- For permission hub 2 debugging only -->
+ <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
</privapp-permissions>
<privapp-permissions package="com.android.phone">
@@ -433,6 +436,14 @@
<permission name="android.car.permission.CAR_DRIVING_STATE" />
<!-- Permissions required for ATS tests - AtsDeviceInfo, AtsAudioDeviceTestCases -->
<permission name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />
+ <!-- Permissions required for ATS tests - AtsDeviceInfo -->
+ <permission name="android.car.permission.CAR_DIAGNOSTICS" />
+ <!-- Permissions required for ATS tests - AtsDeviceInfo -->
+ <permission name="android.car.permission.CLEAR_CAR_DIAGNOSTICS" />
+ <!-- Permissions required for ATS tests - AtsCarHostTestCases -->
+ <permission name="android.car.permission.CONTROL_APP_BLOCKING" />
+ <!-- Permissions required for ATS tests - AtsCarHostTestCases -->
+ <permission name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index 5150043..4cdfbb8 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -317,7 +317,7 @@
# key 367 "KEY_MHP"
# key 368 "KEY_LANGUAGE"
# key 369 "KEY_TITLE"
-# key 370 "KEY_SUBTITLE"
+key 370 CAPTIONS
# key 371 "KEY_ANGLE"
# key 372 "KEY_ZOOM"
# key 373 "KEY_MODE"
@@ -352,7 +352,7 @@
key 402 CHANNEL_UP
key 403 CHANNEL_DOWN
# key 404 "KEY_FIRST"
-# key 405 "KEY_LAST"
+key 405 LAST_CHANNEL
# key 406 "KEY_AB"
# key 407 "KEY_NEXT"
# key 408 "KEY_RESTART"
@@ -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/diff b/diff
deleted file mode 100644
index 5c75d88..0000000
--- a/diff
+++ /dev/null
@@ -1,25 +0,0 @@
-diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
-index fc43882..832dc91 100644
---- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
-+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
-@@ -67,6 +67,7 @@ import java.util.Arrays;
- import java.util.HashSet;
- import java.util.List;
- import java.util.Set;
-+import java.util.NoSuchElementException;
-
- /**
- * This class represents an accessibility client - either an AccessibilityService or a UiAutomation.
-@@ -978,7 +979,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
- /* ignore */
- }
- if (mService != null) {
-- mService.unlinkToDeath(this, 0);
-+ try {
-+ mService.unlinkToDeath(this, 0);
-+ }catch(NoSuchElementException e) {
-+ Slog.e(LOG_TAG, "Failed unregistering death link");
-+ }
- mService = null;
- }
-
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/packages/SystemUI/src/com/android/systemui/wm/DisplayChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/wm/DisplayChangeController.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
index 6eba9ac..3263f79 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayChangeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.wm;
+package com.android.wm.shell.common;
import android.os.Handler;
import android.os.RemoteException;
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/wm/DisplayController.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index 083c243..4189732 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.wm;
+package com.android.wm.shell.common;
import android.annotation.Nullable;
import android.content.Context;
@@ -28,21 +28,16 @@
import android.view.IDisplayWindowListener;
import android.view.IWindowManager;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.wm.DisplayChangeController.OnDisplayChangingListener;
+import com.android.wm.shell.common.DisplayChangeController.OnDisplayChangingListener;
import java.util.ArrayList;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/**
* This module deals with display rotations coming from WM. When WM starts a rotation: after it has
* frozen the screen, it will call into this class. This will then call all registered local
* controllers and give them a chance to queue up task changes to be applied synchronously with that
* rotation.
*/
-@Singleton
public class DisplayController {
private static final String TAG = "DisplayController";
@@ -55,7 +50,7 @@
private final ArrayList<OnDisplaysChangedListener> mDisplayChangedListeners = new ArrayList<>();
/**
- * Get's a display by id from DisplayManager.
+ * Gets a display by id from DisplayManager.
*/
public Display getDisplay(int displayId) {
final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
@@ -169,10 +164,9 @@
}
};
- @Inject
- public DisplayController(Context context, @Main Handler mainHandler,
+ public DisplayController(Context context, Handler handler,
IWindowManager wmService) {
- mHandler = mainHandler;
+ mHandler = handler;
mContext = context;
mWmService = wmService;
mChangeController = new DisplayChangeController(mHandler, mWmService);
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 89f469a..338ece5 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.wm;
+package com.android.wm.shell.common;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -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;
@@ -40,18 +41,12 @@
import android.view.animation.PathInterpolator;
import com.android.internal.view.IInputMethodManager;
-import com.android.systemui.TransactionPool;
-import com.android.systemui.dagger.qualifiers.Main;
import java.util.ArrayList;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/**
* Manages IME control at the display-level. This occurs when IME comes up in multi-window mode.
*/
-@Singleton
public class DisplayImeController implements DisplayController.OnDisplaysChangedListener {
private static final String TAG = "DisplayImeController";
@@ -66,21 +61,20 @@
private static final int DIRECTION_HIDE = 2;
private static final int FLOATING_IME_BOTTOM_INSET = -80;
- SystemWindows mSystemWindows;
- final Handler mHandler;
- final TransactionPool mTransactionPool;
+ protected final IWindowManager mWmService;
+ protected final Handler mHandler;
+ 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<>();
-
- @Inject
- public DisplayImeController(SystemWindows syswin, DisplayController displayController,
- @Main Handler mainHandler, TransactionPool transactionPool) {
+ public DisplayImeController(IWindowManager wmService, DisplayController displayController,
+ Handler mainHandler, TransactionPool transactionPool) {
mHandler = mainHandler;
- mSystemWindows = syswin;
+ mWmService = wmService;
mTransactionPool = transactionPool;
- displayController.addDisplayWindowListener(this);
+ mDisplayController = displayController;
+ mDisplayController.addDisplayWindowListener(this);
}
@Override
@@ -88,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);
}
@@ -103,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 */);
}
@@ -112,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);
}
@@ -270,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) {
}
}
@@ -289,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();
}
@@ -304,9 +298,8 @@
// 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).density()
- * FLOATING_IME_BOTTOM_INSET);
+ final int floatingInset = (int) (mDisplayController.getDisplayLayout(mDisplayId)
+ .density() * FLOATING_IME_BOTTOM_INSET);
mImeFrame.bottom -= floatingInset;
} else if (newFrame.height() != 0) {
// Don't set a new frame if it's empty and hiding -- this maintains continuity
@@ -364,6 +357,7 @@
mAnimation.setInterpolator(INTERPOLATOR);
mAnimation.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled = false;
+
@Override
public void onAnimationStart(Animator animation) {
SurfaceControl.Transaction t = mTransactionPool.acquire();
@@ -386,10 +380,12 @@
t.apply();
mTransactionPool.release(t);
}
+
@Override
public void onAnimationCancel(Animator animation) {
mCancelled = true;
}
+
@Override
public void onAnimationEnd(Animator animation) {
if (DEBUG) Slog.d(TAG, "onAnimationEnd " + mCancelled);
@@ -449,18 +445,19 @@
int IME_ANIMATION_NO_ALPHA = 1;
/** @hide */
- @IntDef(prefix = { "IME_ANIMATION_" }, value = {
+ @IntDef(prefix = {"IME_ANIMATION_"}, value = {
IME_ANIMATION_NO_ALPHA,
})
- @interface ImeAnimationFlags {}
+ @interface ImeAnimationFlags {
+ }
/**
* Called when the IME position is starting to animate.
*
- * @param hiddenTop The y position of the top of the IME surface when it is hidden.
- * @param shownTop The y position of the top of the IME surface when it is shown.
- * @param showing {@code true} when we are animating from hidden to shown, {@code false}
- * when animating from shown to hidden.
+ * @param hiddenTop The y position of the top of the IME surface when it is hidden.
+ * @param shownTop The y position of the top of the IME surface when it is shown.
+ * @param showing {@code true} when we are animating from hidden to shown, {@code false}
+ * when animating from shown to hidden.
* @param isFloating {@code true} when the ime is a floating ime (doesn't inset).
* @return flags that may alter how ime itself is animated (eg. no-alpha).
*/
@@ -476,8 +473,8 @@
*
* @param imeTop The current y position of the top of the IME surface.
*/
- default void onImePositionChanged(int displayId, int imeTop,
- SurfaceControl.Transaction t) {}
+ default void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) {
+ }
/**
* Called when the IME position is done animating.
@@ -485,7 +482,8 @@
* @param cancel {@code true} if this was cancelled. This implies another start is coming.
*/
default void onImeEndPositioning(int displayId, boolean cancel,
- SurfaceControl.Transaction t) {}
+ SurfaceControl.Transaction t) {
+ }
}
public IInputMethodManager getImms() {
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index a341f305..3181dbf 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.wm;
+package com.android.wm.shell.common;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -52,7 +52,7 @@
/**
* Contains information about the layout-properties of a display. This refers to internal layout
- * like insets/cutout/rotation. In general, this can be thought of as the System-UI analog to
+ * like insets/cutout/rotation. In general, this can be thought of as the shell analog to
* DisplayPolicy.
*/
public class DisplayLayout {
@@ -345,9 +345,9 @@
/** Retrieve the statusbar height from resources. */
static int getStatusBarHeight(boolean landscape, Resources res) {
return landscape ? res.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height_landscape)
- : res.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height_portrait);
+ com.android.internal.R.dimen.status_bar_height_landscape)
+ : res.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height_portrait);
}
/** Calculate the DisplayCutout for a particular display size/rotation. */
diff --git a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 21f67ae..8abe9ee 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.wm;
+package com.android.wm.shell.common;
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
@@ -52,23 +52,18 @@
import java.util.HashMap;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/**
- * Represents the "windowing" layer of the System-UI. This layer allows system-ui components to
- * place and manipulate windows without talking to WindowManager.
+ * Represents the "windowing" layer of the WM Shell. This layer allows shell components to place and
+ * manipulate windows without talking to WindowManager.
*/
-@Singleton
public class SystemWindows {
private static final String TAG = "SystemWindows";
private final SparseArray<PerDisplay> mPerDisplay = new SparseArray<>();
- final HashMap<View, SurfaceControlViewHost> mViewRoots = new HashMap<>();
- Context mContext;
- IWindowSession mSession;
- DisplayController mDisplayController;
- IWindowManager mWmService;
+ 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() {
@@ -88,10 +83,7 @@
public void onDisplayRemoved(int displayId) { }
};
- @Inject
- public SystemWindows(Context context, DisplayController displayController,
- IWindowManager wmService) {
- mContext = context;
+ public SystemWindows(DisplayController displayController, IWindowManager wmService) {
mWmService = wmService;
mDisplayController = displayController;
mDisplayController.addDisplayWindowListener(mDisplayListener);
@@ -172,7 +164,7 @@
/**
* Get the IWindow token for a specific root.
*
- * @param windowType A window type from {@link android.view.WindowManager}.
+ * @param windowType A window type from {@link WindowManager}.
*/
IWindow getWindow(int displayId, int windowType) {
PerDisplay pd = mPerDisplay.get(displayId);
@@ -215,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);
@@ -318,7 +310,7 @@
}
}
- class ContainerWindow extends IWindow.Stub {
+ static class ContainerWindow extends IWindow.Stub {
ContainerWindow() {}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/TransactionPool.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TransactionPool.java
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/TransactionPool.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/TransactionPool.java
index 801cf8a..4c34566 100644
--- a/packages/SystemUI/src/com/android/systemui/TransactionPool.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TransactionPool.java
@@ -14,24 +14,19 @@
* limitations under the License.
*/
-package com.android.systemui;
+package com.android.wm.shell.common;
import android.util.Pools;
import android.view.SurfaceControl;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/**
* Provides a synchronized pool of {@link SurfaceControl.Transaction}s to minimize allocations.
*/
-@Singleton
public class TransactionPool {
private final Pools.SynchronizedPool<SurfaceControl.Transaction> mTransactionPool =
new Pools.SynchronizedPool<>(4);
- @Inject
- TransactionPool() {
+ public TransactionPool() {
}
/** Gets a transaction from the pool. */
diff --git a/libs/WindowManager/Shell/tests/Android.bp b/libs/WindowManager/Shell/tests/Android.bp
index 78fa45e..9868879 100644
--- a/libs/WindowManager/Shell/tests/Android.bp
+++ b/libs/WindowManager/Shell/tests/Android.bp
@@ -36,9 +36,6 @@
"libstaticjvmtiagent",
],
- sdk_version: "current",
- platform_apis: true,
-
optimize: {
enabled: false,
},
diff --git a/libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/WindowManagerShellTest.java
similarity index 86%
rename from libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java
rename to libs/WindowManager/Shell/tests/src/com/android/wm/shell/WindowManagerShellTest.java
index 376875b..f1ead3c 100644
--- a/libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java
+++ b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/WindowManagerShellTest.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,13 +14,11 @@
* limitations under the License.
*/
-package com.android.wm.shell.tests;
+package com.android.wm.shell;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.WindowManagerShell;
-
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wm/DisplayLayoutTest.java b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/common/DisplayLayoutTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/wm/DisplayLayoutTest.java
rename to libs/WindowManager/Shell/tests/src/com/android/wm/shell/common/DisplayLayoutTest.java
index 9596a73..2b5b77e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wm/DisplayLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/common/DisplayLayoutTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.wm;
+package com.android.wm.shell.common;
import static android.content.res.Configuration.UI_MODE_TYPE_NORMAL;
import static android.view.Surface.ROTATION_0;
@@ -35,12 +35,11 @@
import androidx.test.filters.SmallTest;
import com.android.internal.R;
-import com.android.systemui.SysuiTestCase;
import org.junit.Test;
@SmallTest
-public class DisplayLayoutTest extends SysuiTestCase {
+public class DisplayLayoutTest {
@Test
public void testInsets() {
diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp b/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp
new file mode 100644
index 0000000..77ef8df
--- /dev/null
+++ b/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp
@@ -0,0 +1,46 @@
+// 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.
+
+cc_fuzz {
+ name: "resourcefile_fuzzer",
+ srcs: [
+ "resourcefile_fuzzer.cpp",
+ ],
+ host_supported: true,
+ corpus: ["corpus/*"],
+ static_libs: ["libgmock"],
+ target: {
+ android: {
+ shared_libs:[
+ "libandroidfw",
+ "libbase",
+ "libcutils",
+ "libutils",
+ "libziparchive",
+ "libui",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libandroidfw",
+ "libbase",
+ "libcutils",
+ "libutils",
+ "libziparchive",
+ "liblog",
+ "libz",
+ ],
+ },
+ },
+}
diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/corpus/resources.arsc b/libs/androidfw/fuzz/resourcefile_fuzzer/corpus/resources.arsc
new file mode 100644
index 0000000..3cf2ea7
--- /dev/null
+++ b/libs/androidfw/fuzz/resourcefile_fuzzer/corpus/resources.arsc
Binary files differ
diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp b/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp
new file mode 100644
index 0000000..96d44ab
--- /dev/null
+++ b/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp
@@ -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.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <string>
+#include <memory>
+
+#include <androidfw/ApkAssets.h>
+#include <androidfw/LoadedArsc.h>
+#include <androidfw/StringPiece.h>
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+using android::ApkAssets;
+using android::LoadedArsc;
+using android::StringPiece;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+
+ std::unique_ptr<const LoadedArsc> loaded_arsc =
+ LoadedArsc::Load(StringPiece(reinterpret_cast<const char*>(data), size));
+
+ return 0;
+}
\ No newline at end of file
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/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 4263521..f4fd1fca 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -53,6 +53,7 @@
* <li> {@link android.media.audiofx.PresetReverb}</li>
* <li> {@link android.media.audiofx.EnvironmentalReverb}</li>
* <li> {@link android.media.audiofx.DynamicsProcessing}</li>
+ * <li> {@link android.media.audiofx.HapticGenerator}</li>
* </ul>
* <p>To apply the audio effect to a specific AudioTrack or MediaPlayer instance,
* the application must specify the audio session ID of that instance when creating the AudioEffect.
@@ -146,6 +147,14 @@
.fromString("7261676f-6d75-7369-6364-28e2fd3ac39e");
/**
+ * UUID for Haptic Generator.
+ */
+ // This is taken from system/media/audio/include/system/audio_effects/effect_hapticgenerator.h
+ @NonNull
+ public static final UUID EFFECT_TYPE_HAPTIC_GENERATOR = UUID
+ .fromString("1411e6d6-aecd-4021-a1cf-a6aceb0d71e5");
+
+ /**
* Null effect UUID. See {@link AudioEffect(UUID, UUID, int, int)} for use.
* @hide
*/
@@ -225,7 +234,8 @@
* {@link AudioEffect#EFFECT_TYPE_BASS_BOOST}, {@link AudioEffect#EFFECT_TYPE_ENV_REVERB},
* {@link AudioEffect#EFFECT_TYPE_EQUALIZER}, {@link AudioEffect#EFFECT_TYPE_NS},
* {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB}, {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER},
- * {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING}.
+ * {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING},
+ * {@link AudioEffect#EFFECT_TYPE_HAPTIC_GENERATOR}.
* </li>
* <li>uuid: UUID for this particular implementation</li>
* <li>connectMode: {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}</li>
@@ -246,8 +256,9 @@
* {@link AudioEffect#EFFECT_TYPE_AGC}, {@link AudioEffect#EFFECT_TYPE_BASS_BOOST},
* {@link AudioEffect#EFFECT_TYPE_ENV_REVERB}, {@link AudioEffect#EFFECT_TYPE_EQUALIZER},
* {@link AudioEffect#EFFECT_TYPE_NS}, {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB}
- * {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER}
- * or {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING}.<br>
+ * {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER},
+ * {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING},
+ * or {@link AudioEffect#EFFECT_TYPE_HAPTIC_GENERATOR}.<br>
* For reverberation, bass boost, EQ and virtualizer, the UUID
* corresponds to the OpenSL ES Interface ID.
*/
@@ -284,7 +295,8 @@
* {@link AudioEffect#EFFECT_TYPE_EQUALIZER}, {@link AudioEffect#EFFECT_TYPE_NS},
* {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB},
* {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER},
- * {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING}.
+ * {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING},
+ * {@link AudioEffect#EFFECT_TYPE_HAPTIC_GENERATOR}.
* @param uuid UUID for this particular implementation
* @param connectMode {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}
* @param name human readable effect name
diff --git a/media/java/android/media/audiofx/HapticGenerator.java b/media/java/android/media/audiofx/HapticGenerator.java
new file mode 100644
index 0000000..f8529eb
--- /dev/null
+++ b/media/java/android/media/audiofx/HapticGenerator.java
@@ -0,0 +1,128 @@
+/*
+ * 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.media.audiofx;
+
+import android.annotation.NonNull;
+import android.media.AudioManager;
+import android.util.Log;
+
+import java.util.UUID;
+
+/**
+ * Haptic Generator(HG).
+ * <p>HG is an audio post-processor which generates haptic data based on the audio channels. The
+ * generated haptic data is sent along with audio data down to the audio HAL, which will require the
+ * device to support audio-coupled-haptic playback. In that case, the effect will only be created on
+ * device supporting audio-coupled-haptic playback. Call {@link HapticGenerator#isAvailable()} to
+ * check if the device supports this effect.
+ * <p>An application can create a HapticGenerator object to initiate and control this audio effect
+ * in the audio framework. An application can set which audio channel to be used to generate
+ * haptic data.
+ * <p>To attach the HapticGenerator to a particular AudioTrack or MediaPlayer, specify the audio
+ * session ID of this AudioTrack or MediaPlayer when constructing the HapticGenerator.
+ * <p>See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions.
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling audio
+ * effects.
+ */
+public class HapticGenerator extends AudioEffect implements AutoCloseable {
+
+ private static final String TAG = "HapticGenerator";
+
+ // For every HapticGenerator, it contains a volume control effect so that the volume control
+ // will always be handled in the effect chain. In that case, the HapticGenerator can generate
+ // haptic data based on the raw audio data.
+ private AudioEffect mVolumeControlEffect;
+
+ public static boolean isAvailable() {
+ return AudioManager.isHapticPlaybackSupported()
+ && AudioEffect.isEffectTypeAvailable(AudioEffect.EFFECT_TYPE_HAPTIC_GENERATOR);
+ }
+
+ /**
+ * Creates a HapticGenerator and attaches it to the given audio session.
+ * Use {@link android.media.AudioTrack#getAudioSessionId()} or
+ * {@link android.media.MediaPlayer#getAudioSessionId()} to
+ * apply this effect on specific AudioTrack or MediaPlayer instance.
+ *
+ * @param audioSession system wide unique audio session identifier. The HapticGenerator will be
+ * applied to the players with the same audio session.
+ * @return HapticGenerator created or null if the device does not support HapticGenerator or
+ * the audio session is invalid.
+ * @throws java.lang.IllegalArgumentException when HapticGenerator is not supported
+ * @throws java.lang.UnsupportedOperationException when the effect library is not loaded.
+ * @throws java.lang.RuntimeException for all other error
+ */
+ public static @NonNull HapticGenerator create(int audioSession) {
+ return new HapticGenerator(audioSession);
+ }
+
+ /**
+ * Class constructor.
+ *
+ * @param audioSession system wide unique audio session identifier. The HapticGenerator will be
+ * attached to the MediaPlayer or AudioTrack in the same audio session.
+ * @throws java.lang.IllegalArgumentException
+ * @throws java.lang.UnsupportedOperationException
+ * @throws java.lang.RuntimeException
+ */
+ private HapticGenerator(int audioSession) {
+ super(EFFECT_TYPE_HAPTIC_GENERATOR, EFFECT_TYPE_NULL, 0, audioSession);
+ mVolumeControlEffect = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+ 0,
+ audioSession);
+ }
+
+ /**
+ * Enable or disable the effect.
+ *
+ * @param enabled the requested enable state
+ * @return {@link #SUCCESS} in case of success, {@link #ERROR_INVALID_OPERATION}
+ * or {@link #ERROR_DEAD_OBJECT} in case of failure.
+ */
+ @Override
+ public int setEnabled(boolean enabled) {
+ int ret = super.setEnabled(enabled);
+ if (ret == SUCCESS) {
+ if (mVolumeControlEffect == null
+ || mVolumeControlEffect.setEnabled(enabled) != SUCCESS) {
+ Log.w(TAG, "Failed to enable volume control effect for HapticGenerator");
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Releases the native AudioEffect resources.
+ */
+ @Override
+ public void release() {
+ if (mVolumeControlEffect != null) {
+ mVolumeControlEffect.release();
+ }
+ super.release();
+ }
+
+ /**
+ * Release the resources that are held by the effect.
+ */
+ @Override
+ public void close() {
+ release();
+ }
+}
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 c14f23f..66637ef 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -5957,6 +5957,7 @@
method public long[] getVibrationPattern();
method public boolean hasUserSetImportance();
method public boolean hasUserSetSound();
+ method public boolean isDemoted();
method public boolean isImportantConversation();
method public void setAllowBubbles(boolean);
method public void setBypassDnd(boolean);
@@ -24061,6 +24062,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
@@ -27666,6 +27668,7 @@
field public static final java.util.UUID EFFECT_TYPE_DYNAMICS_PROCESSING;
field public static final java.util.UUID EFFECT_TYPE_ENV_REVERB;
field public static final java.util.UUID EFFECT_TYPE_EQUALIZER;
+ field @NonNull public static final java.util.UUID EFFECT_TYPE_HAPTIC_GENERATOR;
field public static final java.util.UUID EFFECT_TYPE_LOUDNESS_ENHANCER;
field public static final java.util.UUID EFFECT_TYPE_NS;
field public static final java.util.UUID EFFECT_TYPE_PRESET_REVERB;
@@ -28018,6 +28021,13 @@
field public short numBands;
}
+ public class HapticGenerator extends android.media.audiofx.AudioEffect implements java.lang.AutoCloseable {
+ method public void close();
+ method @NonNull public static android.media.audiofx.HapticGenerator create(int);
+ method public static boolean isAvailable();
+ method public int setEnabled(boolean);
+ }
+
public class LoudnessEnhancer extends android.media.audiofx.AudioEffect {
ctor public LoudnessEnhancer(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.RuntimeException, java.lang.UnsupportedOperationException;
method public float getTargetGain() throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.UnsupportedOperationException;
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 4967426..7b6dceb 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -22,14 +22,13 @@
import android.content.Context;
import android.os.Handler;
import android.os.PowerManager;
+import android.view.IWindowManager;
import com.android.keyguard.KeyguardViewController;
import com.android.systemui.broadcast.BroadcastDispatcher;
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;
@@ -59,16 +58,17 @@
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;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.volume.VolumeDialogComponent;
-import com.android.systemui.wm.DisplayImeController;
import com.android.systemui.wm.DisplaySystemBarsController;
+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.Named;
import javax.inject.Singleton;
@@ -99,10 +99,6 @@
groupManager, configurationController);
}
- @Binds
- abstract DisplayImeController bindDisplayImeController(
- DisplaySystemBarsController displaySystemBarsController);
-
@Singleton
@Provides
@Named(LEAK_REPORT_EMAIL_NAME)
@@ -117,6 +113,31 @@
return new Recents(context, recentsImplementation, commandQueue);
}
+ @Singleton
+ @Provides
+ static TransactionPool provideTransactionPool() {
+ return new TransactionPool();
+ }
+
+ @Singleton
+ @Provides
+ static DisplayController providerDisplayController(Context context, @Main Handler handler,
+ IWindowManager wmService) {
+ return new DisplayController(context, handler, wmService);
+ }
+
+ @Singleton
+ @Provides
+ static SystemWindows provideSystemWindows(DisplayController displayController,
+ IWindowManager wmService) {
+ return new SystemWindows(displayController, wmService);
+ }
+
+ @Singleton
+ @Binds
+ abstract DisplayImeController bindDisplayImeController(
+ DisplaySystemBarsController displaySystemBarsController);
+
@Binds
abstract HeadsUpManager bindHeadsUpManagerPhone(HeadsUpManagerPhone headsUpManagerPhone);
@@ -158,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/FullScreenUserSwitcherViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java
index 10b2b97..1a8f19e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java
@@ -18,6 +18,8 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.car.Car;
+import android.car.user.CarUserManager;
import android.content.Context;
import android.content.res.Resources;
import android.view.View;
@@ -25,6 +27,7 @@
import androidx.recyclerview.widget.GridLayoutManager;
import com.android.systemui.R;
+import com.android.systemui.car.CarServiceProvider;
import com.android.systemui.car.window.OverlayViewController;
import com.android.systemui.car.window.OverlayViewGlobalStateController;
import com.android.systemui.dagger.qualifiers.Main;
@@ -39,7 +42,9 @@
public class FullScreenUserSwitcherViewController extends OverlayViewController {
private final Context mContext;
private final Resources mResources;
+ private final CarServiceProvider mCarServiceProvider;
private final int mShortAnimationDuration;
+ private CarUserManager mCarUserManager;
private UserGridRecyclerView mUserGridView;
private UserGridRecyclerView.UserSelectionListener mUserSelectionListener;
@@ -47,10 +52,16 @@
public FullScreenUserSwitcherViewController(
Context context,
@Main Resources resources,
+ CarServiceProvider carServiceProvider,
OverlayViewGlobalStateController overlayViewGlobalStateController) {
super(R.id.fullscreen_user_switcher_stub, overlayViewGlobalStateController);
mContext = context;
mResources = resources;
+ mCarServiceProvider = carServiceProvider;
+ mCarServiceProvider.addListener(car -> {
+ mCarUserManager = (CarUserManager) car.getCarManager(Car.CAR_USER_SERVICE);
+ registerCarUserManagerIfPossible();
+ });
mShortAnimationDuration = mResources.getInteger(android.R.integer.config_shortAnimTime);
}
@@ -63,6 +74,7 @@
mUserGridView.setLayoutManager(layoutManager);
mUserGridView.buildAdapter();
mUserGridView.setUserSelectionListener(mUserSelectionListener);
+ registerCarUserManagerIfPossible();
}
@Override
@@ -91,18 +103,6 @@
}
/**
- * Invalidate underlying view.
- */
- void invalidate() {
- if (getLayout() == null) {
- // layout hasn't been inflated.
- return;
- }
-
- getLayout().invalidate();
- }
-
- /**
* Set {@link UserGridRecyclerView.UserSelectionListener}.
*/
void setUserGridSelectionListener(
@@ -110,15 +110,9 @@
mUserSelectionListener = userGridSelectionListener;
}
- /**
- * Returns {@code true} when layout is visible.
- */
- boolean isVisible() {
- if (getLayout() == null) {
- // layout hasn't been inflated.
- return false;
+ private void registerCarUserManagerIfPossible() {
+ if (mUserGridView != null && mCarUserManager != null) {
+ mUserGridView.setCarUserManager(mCarUserManager);
}
-
- return getLayout().getVisibility() == View.VISIBLE;
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java
index 2ff6670..d0a2aeb 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java
@@ -24,11 +24,15 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
-import android.car.userlib.CarUserManagerHelper;
+import android.car.user.CarUserManager;
+import android.car.user.UserCreationResult;
+import android.car.user.UserSwitchResult;
+import android.car.userlib.UserHelper;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
@@ -40,7 +44,9 @@
import android.os.AsyncTask;
import android.os.UserHandle;
import android.os.UserManager;
+import android.sysprop.CarProperties;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -54,6 +60,7 @@
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.UserIcons;
import com.android.systemui.R;
@@ -61,6 +68,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
@@ -68,9 +76,12 @@
* One of the uses of this is for the lock screen in auto.
*/
public class UserGridRecyclerView extends RecyclerView {
+ private static final String TAG = UserGridRecyclerView.class.getSimpleName();
+ private static final int TIMEOUT_MS = CarProperties.user_hal_timeout().orElse(5_000) + 500;
+
private UserSelectionListener mUserSelectionListener;
private UserAdapter mAdapter;
- private CarUserManagerHelper mCarUserManagerHelper;
+ private CarUserManager mCarUserManager;
private UserManager mUserManager;
private Context mContext;
private UserIconProvider mUserIconProvider;
@@ -85,7 +96,6 @@
public UserGridRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
- mCarUserManagerHelper = new CarUserManagerHelper(mContext);
mUserManager = UserManager.get(mContext);
mUserIconProvider = new UserIconProvider();
@@ -184,6 +194,11 @@
mUserSelectionListener = userSelectionListener;
}
+ /** Sets a {@link CarUserManager}. */
+ public void setCarUserManager(CarUserManager carUserManager) {
+ mCarUserManager = carUserManager;
+ }
+
private void onUsersUpdate() {
mAdapter.clearUsers();
mAdapter.updateUsers(createUserRecords(getUsersForUserGrid()));
@@ -273,7 +288,9 @@
notifyUserSelected(userRecord);
UserInfo guest = createNewOrFindExistingGuest(mContext);
if (guest != null) {
- mCarUserManagerHelper.switchToUser(guest);
+ if (!switchUser(guest.id)) {
+ Log.e(TAG, "Failed to switch to guest user: " + guest.id);
+ }
}
break;
case UserRecord.ADD_USER:
@@ -289,7 +306,9 @@
// If the user doesn't want to be a guest or add a user, switch to the user
// selected
notifyUserSelected(userRecord);
- mCarUserManagerHelper.switchToUser(userRecord.mInfo);
+ if (!switchUser(userRecord.mInfo.id)) {
+ Log.e(TAG, "Failed to switch users: " + userRecord.mInfo.id);
+ }
}
});
@@ -430,8 +449,9 @@
*/
@Nullable
public UserInfo createNewOrFindExistingGuest(Context context) {
+ AndroidFuture<UserCreationResult> future = mCarUserManager.createGuest(mGuestName);
// CreateGuest will return null if a guest already exists.
- UserInfo newGuest = mUserManager.createGuest(context, mGuestName);
+ UserInfo newGuest = getUserInfo(future);
if (newGuest != null) {
new UserIconProvider().assignDefaultIcon(
mUserManager, context.getResources(), newGuest);
@@ -444,7 +464,6 @@
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == BUTTON_POSITIVE) {
- notifyUserSelected(mAddUserRecord);
new AddNewUserTask().execute(mNewUserName);
} else if (which == BUTTON_NEGATIVE) {
// Enable the add button only if cancel
@@ -462,11 +481,77 @@
}
}
+ @Nullable
+ private UserInfo getUserInfo(AndroidFuture<UserCreationResult> future) {
+ UserCreationResult userCreationResult;
+ try {
+ userCreationResult = future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (Exception e) {
+ Log.w(TAG, "Could not create user.", e);
+ return null;
+ }
+
+ if (userCreationResult == null) {
+ Log.w(TAG, "Timed out while creating user: " + TIMEOUT_MS + "ms");
+ return null;
+ }
+ if (!userCreationResult.isSuccess() || userCreationResult.getUser() == null) {
+ Log.w(TAG, "Could not create user: " + userCreationResult);
+ return null;
+ }
+
+ return userCreationResult.getUser();
+ }
+
+ private boolean switchUser(@UserIdInt int userId) {
+ AndroidFuture<UserSwitchResult> userSwitchResultFuture =
+ mCarUserManager.switchUser(userId);
+ UserSwitchResult userSwitchResult;
+ try {
+ userSwitchResult = userSwitchResultFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (Exception e) {
+ Log.w(TAG, "Could not switch user.", e);
+ return false;
+ }
+
+ if (userSwitchResult == null) {
+ Log.w(TAG, "Timed out while switching user: " + TIMEOUT_MS + "ms");
+ return false;
+ }
+ if (!userSwitchResult.isSuccess()) {
+ Log.w(TAG, "Could not switch user: " + userSwitchResult);
+ return false;
+ }
+
+ return true;
+ }
+
+ // TODO(b/161539497): Replace AsyncTask with standard {@link java.util.concurrent} code.
private class AddNewUserTask extends AsyncTask<String, Void, UserInfo> {
@Override
protected UserInfo doInBackground(String... userNames) {
- return mCarUserManagerHelper.createNewNonAdminUser(userNames[0]);
+ AndroidFuture<UserCreationResult> future = mCarUserManager.createUser(userNames[0],
+ /* flags= */ 0);
+ try {
+ UserInfo user = getUserInfo(future);
+ if (user != null) {
+ UserHelper.setDefaultNonAdminRestrictions(mContext, user,
+ /* enable= */ true);
+ UserHelper.assignDefaultIcon(mContext, user);
+ mAddUserRecord = new UserRecord(user, UserRecord.ADD_USER);
+ return user;
+ } else {
+ Log.e(TAG, "Failed to create user in the background");
+ return user;
+ }
+ } catch (Exception e) {
+ if (e instanceof InterruptedException) {
+ Thread.currentThread().interrupt();
+ }
+ Log.e(TAG, "Error creating new user: ", e);
+ }
+ return null;
}
@Override
@@ -476,7 +561,11 @@
@Override
protected void onPostExecute(UserInfo user) {
if (user != null) {
- mCarUserManagerHelper.switchToUser(user);
+ notifyUserSelected(mAddUserRecord);
+ mAddUserView.setEnabled(true);
+ if (!switchUser(user.id)) {
+ Log.e(TAG, "Failed to switch to new user: " + user.id);
+ }
}
}
}
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 a831464..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;
@@ -29,8 +31,10 @@
import androidx.annotation.VisibleForTesting;
-import com.android.systemui.TransactionPool;
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.TransactionPool;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -47,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();
@@ -82,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);
}
@@ -152,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 29cc8ee..391f75e 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
@@ -33,7 +33,8 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.TransactionPool;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.TransactionPool;
import org.junit.Before;
import org.junit.Test;
@@ -51,8 +52,6 @@
private static final int DISPLAY_ID = 1;
@Mock
- private SystemWindows mSystemWindows;
- @Mock
private IWindowManager mIWindowManager;
@Mock
private DisplayController mDisplayController;
@@ -64,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/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
index ac7a121..121f549 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
@@ -38,7 +38,10 @@
"/odm/etc/NOTICE.xml.gz",
"/oem/etc/NOTICE.xml.gz",
"/product/etc/NOTICE.xml.gz",
- "/system_ext/etc/NOTICE.xml.gz"};
+ "/system_ext/etc/NOTICE.xml.gz",
+ "/vendor_dlkm/etc/NOTICE.xml.gz",
+ "/odm_dlkm/etc/NOTICE.xml.gz",
+ };
static final String NOTICE_HTML_FILE_NAME = "NOTICE.html";
private final Context mContext;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index 6269a71..fd986e5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -90,7 +90,7 @@
return frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
}
- // Used for dummy pref.
+ // Used for fake pref.
public AccessPointPreference(Context context, AttributeSet attrs) {
super(context, attrs);
mFrictionSld = null;
@@ -142,7 +142,7 @@
public void onBindViewHolder(final PreferenceViewHolder view) {
super.onBindViewHolder(view);
if (mAccessPoint == null) {
- // Used for dummy pref.
+ // Used for fake pref.
return;
}
Drawable drawable = getIcon();
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 3015397..bf5ab1c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -256,7 +256,7 @@
}
/**
- * Sanity warning: this wipes out mScoreCache, so use with extreme caution
+ * Validity warning: this wipes out mScoreCache, so use with extreme caution
* @param workThread substitute Handler thread, for testing purposes only
*/
@VisibleForTesting
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index 0e6a60b..1ace0b4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -118,7 +118,7 @@
final int maxDisplayedScans = 4;
int num5 = 0; // number of scanned BSSID on 5GHz band
int num24 = 0; // number of scanned BSSID on 2.4Ghz band
- int numBlackListed = 0;
+ int numBlockListed = 0;
// TODO: sort list by RSSI or age
long nowMs = SystemClock.elapsedRealtime();
@@ -170,8 +170,8 @@
}
visibility.append(scans5GHz.toString());
}
- if (numBlackListed > 0) {
- visibility.append("!").append(numBlackListed);
+ if (numBlockListed > 0) {
+ visibility.append("!").append(numBlockListed);
}
visibility.append("]");
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 27576b1..ef3bdbc9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -866,9 +866,6 @@
Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS,
GlobalSettingsProto.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS);
dumpSetting(s, p,
- Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS,
- GlobalSettingsProto.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS);
- dumpSetting(s, p,
Settings.Global.KEEP_PROFILE_IN_BACKGROUND,
GlobalSettingsProto.KEEP_PROFILE_IN_BACKGROUND);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index b90b9c1..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,
@@ -311,7 +311,6 @@
Settings.Global.INTENT_FIREWALL_UPDATE_METADATA_URL,
Settings.Global.JOB_SCHEDULER_CONSTANTS,
Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS,
- Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS,
Settings.Global.KEEP_PROFILE_IN_BACKGROUND,
Settings.Global.KERNEL_CPU_THREAD_READER,
Settings.Global.LANG_ID_UPDATE_CONTENT_URL,
@@ -591,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.
@@ -746,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/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 7f7afcb..aa96087 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -214,6 +214,9 @@
<!-- Permission needed to test tcp keepalive offload. -->
<uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
+ <!-- Permission needed for CTS test - UnsupportedErrorDialogTests -->
+ <uses-permission android:name="android.permission.RESET_APP_ERRORS" />
+
<!-- Permission needed to run keyguard manager tests in CTS -->
<uses-permission android:name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS" />
@@ -321,6 +324,14 @@
<uses-permission android:name="android.car.permission.CAR_DRIVING_STATE" />
<!-- Permissions required for ATS tests - AtsDeviceInfo, AtsAudioDeviceTestCases -->
<uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />
+ <!-- Permissions required for ATS tests - AtsDeviceInfo -->
+ <uses-permission android:name="android.car.permission.CAR_DIAGNOSTICS" />
+ <!-- Permissions required for ATS tests - AtsDeviceInfo -->
+ <uses-permission android:name="android.car.permission.CLEAR_CAR_DIAGNOSTICS" />
+ <!-- Permissions required for ATS tests - AtsCarHostTestCases -->
+ <uses-permission android:name="android.car.permission.CONTROL_APP_BLOCKING" />
+ <!-- Permissions required for ATS tests - AtsCarHostTestCases -->
+ <uses-permission android:name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION" />
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
index d2f168e..d69f3d6 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
@@ -333,6 +333,9 @@
@Override
public void onDestroy() {
+ if (mHandler != null) {
+ mHandler.removeCallbacksAndMessages(null);
+ }
if (mCursor != null) {
mCursor.close();
mCursor = null;
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index dfc4758..2fbd9ba 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -45,7 +45,7 @@
"WindowManager-Shell",
"SystemUIPluginLib",
"SystemUISharedLib",
- "SystemUI-statsd",
+ "SystemUI-statsd",
"SettingsLib",
"androidx.viewpager2_viewpager2",
"androidx.legacy_legacy-support-v4",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6aa233b..98d35532 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-keyguard/drawable/kg_emergency_button_background.xml b/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml
new file mode 100644
index 0000000..cc2089f
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml
@@ -0,0 +1,32 @@
+<?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.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/wallpaperTextColorSecondary">
+ <item android:id="@android:id/background">
+ <shape
+ android:color="@android:color/transparent">
+ <stroke android:width="1dp" android:color="?attr/wallpaperTextColorSecondary"/>
+ <corners android:radius="24dp"/>
+ </shape>
+ </item>
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="?attr/wallpaperTextColorSecondary"/>
+ <corners android:radius="24dp"/>
+ </shape>
+ </item>
+</ripple>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
index 3018a02..370576b 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
@@ -33,6 +33,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/Keyguard.TextView"
+ android:layout_marginBottom="8dp"
android:singleLine="true"
android:ellipsize="marquee"
android:visibility="gone"
@@ -42,11 +43,9 @@
<com.android.keyguard.EmergencyButton
android:id="@+id/emergency_call_button"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginTop="@dimen/eca_overlap"
+ android:layout_height="32dp"
+ android:layout_marginBottom="12dp"
android:text="@*android:string/lockscreen_emergency_call"
- style="@style/Keyguard.TextView.EmergencyButton"
- android:textAllCaps="@bool/kg_use_all_caps" />
+ style="@style/Keyguard.TextView.EmergencyButton" />
</com.android.keyguard.EmergencyCarrierArea>
diff --git a/packages/SystemUI/res-keyguard/values-af/strings.xml b/packages/SystemUI/res-keyguard/values-af/strings.xml
index 8a1f6de..92dd9fd 100644
--- a/packages/SystemUI/res-keyguard/values-af/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-af/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Geen diens nie."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Wissel invoermetode"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Vliegtuigmodus"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN word vereis om vir opdatering voor te berei"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Patroon word vereis om vir opdatering voor te berei"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Wagwoord word vereis om vir opdatering voor te berei"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Patroon word vereis nadat toestel herbegin het"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN word vereis nadat toestel herbegin het"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Wagwoord word vereis nadat toestel herbegin het"</string>
diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml
index 0a4aee5..f94c20f 100644
--- a/packages/SystemUI/res-keyguard/values-am/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-am/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"ከአገልግሎት መስጫ ክልል ውጪ።"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"የግቤት ስልት ቀይር"</string>
<string name="airplane_mode" msgid="2528005343938497866">"የአውሮፕላን ሁነታ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"ለዝማኔ ለማዘጋጀት ፒን ያስፈልጋል"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"ለዝማኔ ለማዘጋጀት ሥርዓተ ጥለት ያስፈልጋል"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"ለዝማኔ ለማዘጋጀት የይለፍ ቃል ያስፈልጋል"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"መሣሪያ ዳግም ከጀመረ በኋላ ሥርዓተ ጥለት ያስፈልጋል"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"መሣሪያ ዳግም ከተነሳ በኋላ ፒን ያስፈልጋል"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"መሣሪያ ዳግም ከጀመረ በኋላ የይለፍ ቃል ያስፈልጋል"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index 491dc39a..6d86a78 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -113,9 +113,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"لا تتوفر خدمة."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"تبديل أسلوب الإدخال"</string>
<string name="airplane_mode" msgid="2528005343938497866">"وضع الطائرة"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"يجب إدخال رقم التعريف الشخصي للتحضير للتحديث."</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"يجب رسم النقش للتحضير للتحديث."</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"يجب إدخال كلمة المرور للتحضير للتحديث."</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"يجب رسم النقش بعد إعادة تشغيل الجهاز"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"يجب إدخال رقم التعريف الشخصي بعد إعادة تشغيل الجهاز"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"يجب إدخال كلمة المرور بعد إعادة تشغيل الجهاز"</string>
diff --git a/packages/SystemUI/res-keyguard/values-as/strings.xml b/packages/SystemUI/res-keyguard/values-as/strings.xml
index 4367efb..3b51e48 100644
--- a/packages/SystemUI/res-keyguard/values-as/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-as/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"কোনো সেৱা নাই।"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ইনপুট পদ্ধতি সলনি কৰক"</string>
<string name="airplane_mode" msgid="2528005343938497866">"এয়াৰপ্লেন ম\'ড"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"আপডে\'টৰ বাবে সাজু হ\'বলৈ পিনৰ আৱশ্যক"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"আপডে\'টৰ বাবে সাজু হ\'বলৈ আর্হিৰ আৱশ্যক"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"আপডে\'টৰ বাবে সাজু হ\'বলৈ পাছৱৰ্ডৰ আৱশ্যক"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ডিভাইচ ৰিষ্টাৰ্ট হোৱাৰ পিছত আৰ্হি দিয়াটো বাধ্যতামূলক"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ডিভাইচ ৰিষ্টাৰ্ট হোৱাৰ পিছত পিন দিয়াটো বাধ্যতামূলক"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ডিভাইচ ৰিষ্টাৰ্ট হোৱাৰ পিছত পাছৱৰ্ড দিয়াটো বাধ্যতামূলক"</string>
diff --git a/packages/SystemUI/res-keyguard/values-az/strings.xml b/packages/SystemUI/res-keyguard/values-az/strings.xml
index d89bf6a..ea07c3d 100644
--- a/packages/SystemUI/res-keyguard/values-az/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-az/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Xidmət yoxdur."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Daxiletmə metoduna keçin"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Təyyarə rejimi"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Güncəlləməyə hazırlıq üçün PIN kod tələb olunur"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Güncəlləməyə hazırlıq üçün model tələb olunur"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Güncəlləməyə hazırlıq üçün parol tələb olunur"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Cihaz yenidən başladıqdan sonra model tələb olunur"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Cihaz yeniden başladıqdan sonra PIN tələb olunur"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Cihaz yeniden başladıqdan sonra parol tələb olunur"</string>
diff --git a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
index 656e323..e206958 100644
--- a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
@@ -104,9 +104,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Mreža nije dostupna."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Promeni metod unosa"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Režim rada u avionu"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN je obavezan radi pripreme za ažuriranje"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Šablon je obavezan radi pripreme za ažuriranje"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Lozinka je obavezna radi pripreme za ažuriranje"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Treba da unesete šablon kada se uređaj ponovo pokrene"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Treba da unesete PIN kada se uređaj ponovo pokrene"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Treba da unesete lozinku kada se uređaj ponovo pokrene"</string>
diff --git a/packages/SystemUI/res-keyguard/values-be/strings.xml b/packages/SystemUI/res-keyguard/values-be/strings.xml
index 07b6f35..569e705 100644
--- a/packages/SystemUI/res-keyguard/values-be/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-be/strings.xml
@@ -107,9 +107,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Не абслугоўваецца."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Пераключэнне рэжыму ўводу"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Рэжым палёту"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Для падрыхтоўкі да абнаўлення неабходна ўвесці PIN-код"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Для падрыхтоўкі да абнаўлення неабходна ўвесці ўзор разблакіроўкі"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Для падрыхтоўкі да абнаўлення неабходна ўвесці пароль"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Пасля перазапуску прылады патрабуецца ўзор"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Пасля перазапуску прылады патрабуецца PIN-код"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Пасля перазапуску прылады патрабуецца пароль"</string>
diff --git a/packages/SystemUI/res-keyguard/values-bg/strings.xml b/packages/SystemUI/res-keyguard/values-bg/strings.xml
index a8c64f5..d015be3 100644
--- a/packages/SystemUI/res-keyguard/values-bg/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bg/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Няма покритие."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Превключване на метода на въвеждане"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Самолетен режим"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"За подготовката за актуализация се изисква ПИН код"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"За подготовката за актуализация се изисква фигура"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"За подготовката за актуализация се изисква парола"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"След рестартиране на устройството се изисква фигура"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"След рестартиране на устройството се изисква ПИН код"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"След рестартиране на устройството се изисква парола"</string>
diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml
index 479e83a..8eae6e6 100644
--- a/packages/SystemUI/res-keyguard/values-bn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"কোনো পরিষেবা নেই।"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ইনপুট পদ্ধতি পরিবর্তন করুন"</string>
<string name="airplane_mode" msgid="2528005343938497866">"বিমান মোড"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"আপডেট প্রস্তুত করতে পিন দরকার"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"আপডেট প্রস্তুত করতে প্যাটার্ন দরকার"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"আপডেট প্রস্তুত করতে পাসওয়ার্ড দরকার"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ডিভাইসটি পুনরায় চালু হওয়ার পর প্যাটার্নের প্রয়োজন হবে"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ডিভাইসটি পুনরায় চালু হওয়ার পর পিন প্রয়োজন হবে"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ডিভাইসটি পুনরায় চালু হওয়ার পর পাসওয়ার্ডের প্রয়োজন হবে"</string>
diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml
index ada4c13..286b08b 100644
--- a/packages/SystemUI/res-keyguard/values-bs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml
@@ -104,9 +104,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Nema mreže."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Promjena načina unosa"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Način rada u avionu"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Za pripremu ažuriranja potreban je PIN"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Za pripremu ažuriranja potreban je uzorak"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Za pripremu ažuriranja potrebna je lozinka"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Potreban je uzorak nakon što se uređaj ponovo pokrene"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Potreban je PIN nakon što se uređaj ponovo pokrene"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Potrebna je lozinka nakon što se uređaj ponovo pokrene"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml
index 6f5b682..cb7fa37 100644
--- a/packages/SystemUI/res-keyguard/values-ca/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Sense servei"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Canvia el mètode d\'introducció"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Mode d\'avió"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Cal introduir el PIN per preparar l\'actualització"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Cal introduir el patró per preparar l\'actualització"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Cal introduir la contrasenya per preparar l\'actualització"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Cal introduir el patró quan es reinicia el dispositiu"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Cal introduir el PIN quan es reinicia el dispositiu"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Cal introduir la contrasenya quan es reinicia el dispositiu"</string>
diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml
index a2f79ad..4f0c0ff 100644
--- a/packages/SystemUI/res-keyguard/values-cs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml
@@ -107,9 +107,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Žádný signál"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Přepnout metodu zadávání"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Režim Letadlo"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Příprava na aktualizaci vyžaduje PIN"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Příprava na aktualizaci vyžaduje gesto"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Příprava na aktualizaci vyžaduje heslo"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Po restartování zařízení je vyžadováno gesto"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Po restartování zařízení je vyžadován kód PIN"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Po restartování zařízení je vyžadováno heslo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-da/strings.xml b/packages/SystemUI/res-keyguard/values-da/strings.xml
index ef06269..e486fc6 100644
--- a/packages/SystemUI/res-keyguard/values-da/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-da/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Ingen dækning."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Skift indtastningsmetode"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Flytilstand"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Du skal angive din pinkode for at forberede opdateringen"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Du skal angive dit mønster for at forberede opdateringen"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Du skal angive din adgangskode for at forberede opdateringen"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Du skal angive et mønster, når du har genstartet enheden"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Der skal angives en pinkode efter genstart af enheden"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Der skal angives en adgangskode efter genstart af enheden"</string>
diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml
index fdfce1f..06d012f 100644
--- a/packages/SystemUI/res-keyguard/values-de/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-de/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Dienst nicht verfügbar"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Eingabemethode wechseln"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Flugmodus"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Zur Vorbereitung auf das Update ist eine PIN erforderlich"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Zur Vorbereitung auf das Update ist ein Muster erforderlich"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Zur Vorbereitung auf das Update ist ein Passwort erforderlich"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Nach dem Neustart des Geräts ist die Eingabe des Musters erforderlich"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Nach dem Neustart des Geräts ist die Eingabe der PIN erforderlich"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Nach dem Neustart des Geräts ist die Eingabe des Passworts erforderlich"</string>
diff --git a/packages/SystemUI/res-keyguard/values-el/strings.xml b/packages/SystemUI/res-keyguard/values-el/strings.xml
index 8e4578f..1764284 100644
--- a/packages/SystemUI/res-keyguard/values-el/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-el/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Καμία υπηρεσία."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Εναλλαγή μεθόδου εισαγωγής"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Λειτουργία πτήσης"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Απαιτείται PIN για την προετοιμασία για ενημέρωση"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Απαιτείται μοτίβο για την προετοιμασία για ενημέρωση"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Απαιτείται κωδικός πρόσβασης για την προετοιμασία για ενημέρωση"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Απαιτείται μοτίβο μετά από την επανεκκίνηση της συσκευής"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Απαιτείται PIN μετά από την επανεκκίνηση της συσκευής"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Απαιτείται κωδικός πρόσβασης μετά από την επανεκκίνηση της συσκευής"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
index 21cfe48..92a1594 100644
--- a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"No service"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Switch input method"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Aeroplane mode"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN required to prepare for update"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Pattern required to prepare for update"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Password required to prepare for update"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pattern required after device restarts"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN required after device restarts"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Password required after device restarts"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
index 921ba6b..719f1a1 100644
--- a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"No service"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Switch input method"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Airplane mode"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN required to prepare for update"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Pattern required to prepare for update"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Password required to prepare for update"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pattern required after device restarts"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN required after device restarts"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Password required after device restarts"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
index 21cfe48..92a1594 100644
--- a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"No service"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Switch input method"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Aeroplane mode"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN required to prepare for update"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Pattern required to prepare for update"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Password required to prepare for update"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pattern required after device restarts"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN required after device restarts"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Password required after device restarts"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
index 21cfe48..92a1594 100644
--- a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"No service"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Switch input method"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Aeroplane mode"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN required to prepare for update"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Pattern required to prepare for update"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Password required to prepare for update"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pattern required after device restarts"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN required after device restarts"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Password required after device restarts"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
index fc59d0d..975b1f6 100644
--- a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"No service."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Switch input method"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Airplane mode"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN required to prepare for update"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Pattern required to prepare for update"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Password required to prepare for update"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pattern required after device restarts"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN required after device restarts"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Password required after device restarts"</string>
diff --git a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
index ab0c8f3..25ab615 100644
--- a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Sin servicio"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Cambiar método de entrada"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Modo de avión"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Se requiere el PIN para actualizar el sistema"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Se requiere el patrón para actualizar el sistema"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Se requiere la contraseña para actualizar el sistema"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Se requiere el patrón después de reiniciar el dispositivo"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Se requiere el PIN después de reiniciar el dispositivo"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Se requiere la contraseña después de reiniciar el dispositivo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-es/strings.xml b/packages/SystemUI/res-keyguard/values-es/strings.xml
index 38451c7..0754681 100644
--- a/packages/SystemUI/res-keyguard/values-es/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Sin servicio"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Cambiar método de introducción"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Modo avión"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Debes introducir el PIN para prepararte para la actualización"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Debes dibujar el patrón para prepararte para la actualización"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Debes introducir la contraseña para prepararte para la actualización"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Debes introducir el patrón después de reiniciar el dispositivo"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Debes introducir el PIN después de reiniciar el dispositivo"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Debes introducir la contraseña después de reiniciar el dispositivo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-et/strings.xml b/packages/SystemUI/res-keyguard/values-et/strings.xml
index f8ad18b..331a95c 100644
--- a/packages/SystemUI/res-keyguard/values-et/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-et/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Teenus puudub."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Vaheta sisestusmeetodit"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Lennukirežiim"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Värskendamiseks ettevalmistuste tegemiseks tuleb sisestada PIN-kood"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Värskendamiseks ettevalmistuste tegemiseks tuleb sisestada muster"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Värskendamiseks ettevalmistuste tegemiseks tuleb sisestada parool"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pärast seadme taaskäivitamist tuleb sisestada muster"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Pärast seadme taaskäivitamist tuleb sisestada PIN-kood"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Pärast seadme taaskäivitamist tuleb sisestada parool"</string>
diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml
index 8510bee..3ff224b 100644
--- a/packages/SystemUI/res-keyguard/values-eu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Ez dago konektatuta inongo saretara."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Aldatu idazketa-metodoa"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Hegaldi modua"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN kodea behar da eguneratzea prestatzeko"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Eredua behar da eguneratzea prestatzeko"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Pasahitza behar da eguneratzea prestatzeko"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Eredua marraztu beharko duzu gailua berrabiarazten denean"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN kodea idatzi beharko duzu gailua berrabiarazten denean"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Pasahitza idatzi beharko duzu gailua berrabiarazten denean"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml
index 43d3214..5e69636 100644
--- a/packages/SystemUI/res-keyguard/values-fa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"سرویسی وجود ندارد."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"تغییر روش ورودی"</string>
<string name="airplane_mode" msgid="2528005343938497866">"حالت هواپیما"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"آمادهسازی برای بهروزرسانی به پین نیاز دارد"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"آمادهسازی برای بهروزرسانی به الگو نیاز دارد"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"آمادهسازی برای بهروزرسانی به گذرواژه نیاز دارد"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"بعد از بازنشانی دستگاه باید الگو وارد شود"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"بعد از بازنشانی دستگاه باید پین وارد شود"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"بعد از بازنشانی دستگاه باید گذرواژه وارد شود"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml
index 7dc12c9..54bc4d8 100644
--- a/packages/SystemUI/res-keyguard/values-fi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Ei yhteyttä"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Vaihda syöttötapaa."</string>
<string name="airplane_mode" msgid="2528005343938497866">"Lentokonetila"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Päivitykseen valmistautuminen edellyttää PIN-koodia"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Päivitykseen valmistautuminen edellyttää kuviota"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Päivitykseen valmistautuminen edellyttää salasanaa"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Kuvio vaaditaan laitteen uudelleenkäynnistyksen jälkeen."</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN-koodi vaaditaan laitteen uudelleenkäynnistyksen jälkeen."</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Salasana vaaditaan laitteen uudelleenkäynnistyksen jälkeen."</string>
diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
index f093c17..3e858c2 100644
--- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Aucun service"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Changer de méthode d\'entrée"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Mode Avion"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Le NIP est nécessaire pour préparer la mise à jour"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Le schéma est nécessaire pour préparer la mise à jour"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Le mot de passe est nécessaire pour préparer la mise à jour"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Le schéma est exigé après le redémarrage de l\'appareil"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Le NIP est exigé après le redémarrage de l\'appareil"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Le mot de passe est exigé après le redémarrage de l\'appareil"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fr/strings.xml b/packages/SystemUI/res-keyguard/values-fr/strings.xml
index d6d5a32..8551fab 100644
--- a/packages/SystemUI/res-keyguard/values-fr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Aucun service."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Changer le mode de saisie"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Mode Avion"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Veuillez saisir le code pour lancer la préparation de la mise à jour"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Veuillez saisir le schéma pour lancer la préparation de la mise à jour"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Veuillez saisir le mot de passe pour lancer la préparation de la mise à jour"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Veuillez dessiner le schéma après le redémarrage de l\'appareil"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Veuillez saisir le code après le redémarrage de l\'appareil"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Veuillez saisir le mot de passe après le redémarrage de l\'appareil"</string>
diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml
index f5d5bb4..4607981 100644
--- a/packages/SystemUI/res-keyguard/values-gl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Non hai servizo."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Cambia o método de introdución"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Modo avión"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Necesítase o PIN para preparar o dispositivo co fin de actualizalo"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Necesítase o padrón para preparar o dispositivo co fin de actualizalo"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Necesítase o contrasinal para preparar o dispositivo co fin de actualizalo"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"É necesario o padrón despois do reinicio do dispositivo"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"É necesario o PIN despois do reinicio do dispositivo"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"É necesario o contrasinal despois do reinicio do dispositivo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml
index 29e2fe0..b02d3d9 100644
--- a/packages/SystemUI/res-keyguard/values-gu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gu/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"કોઈ સેવા નથી."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ઇનપુટ પદ્ધતિ સ્વિચ કરો"</string>
<string name="airplane_mode" msgid="2528005343938497866">"એરપ્લેન મોડ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"અપડેટ માટે તૈયાર કરવા માટે પિન જરુરી છે"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"અપડેટ માટે તૈયાર કરવા માટે પૅટર્ન જરુરી છે"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"અપડેટ માટે તૈયાર કરવા માટે પાસવર્ડ જરુરી છે"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ઉપકરણનો પુનઃપ્રારંભ થાય તે પછી પૅટર્ન જરૂરી છે"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ઉપકરણનો પુનઃપ્રારંભ થાય તે પછી પિન જરૂરી છે"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ઉપકરણનો પુનઃપ્રારંભ થાય તે પછી પાસવર્ડ જરૂરી છે"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml
index d26c79f..f6b15de 100644
--- a/packages/SystemUI/res-keyguard/values-hi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"कोई सेवा नहीं."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"इनपुट का तरीका बदलें"</string>
<string name="airplane_mode" msgid="2528005343938497866">"हवाई जहाज़ मोड"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"अपडेट के लिए पिन डालना ज़रूरी है"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"अपडेट के लिए पैटर्न डालना ज़रूरी है"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"अपडेट के लिए पासवर्ड डालना ज़रूरी है"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"डिवाइस फिर से चालू होने के बाद पैटर्न ज़रूरी है"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"डिवाइस फिर से चालू होने के बाद पिन ज़रूरी है"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"डिवाइस फिर से चालू होने के बाद पासवर्ड ज़रूरी है"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hr/strings.xml b/packages/SystemUI/res-keyguard/values-hr/strings.xml
index c8dd9b0..49db3f88 100644
--- a/packages/SystemUI/res-keyguard/values-hr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hr/strings.xml
@@ -104,9 +104,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Nema usluge."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Promjena načina unosa"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Način rada u zrakoplovu"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Za pripremu ažuriranja potreban je PIN"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Za pripremu ažuriranja potreban je uzorak"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Za pripremu ažuriranja potrebna je zaporka"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Nakon ponovnog pokretanja uređaja morate unijeti uzorak"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Nakon ponovnog pokretanja uređaja morate unijeti PIN"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Nakon ponovnog pokretanja uređaja morate unijeti zaporku"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hu/strings.xml b/packages/SystemUI/res-keyguard/values-hu/strings.xml
index f0023d2..c26998f 100644
--- a/packages/SystemUI/res-keyguard/values-hu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hu/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Nincs szolgáltatás."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Beviteli módszer váltása"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Repülős üzemmód"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"A frissítésre való felkészüléshez meg kell adni a PIN-kódot"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"A frissítésre való felkészüléshez meg kell adni a mintát"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"A frissítésre való felkészüléshez meg kell adni a jelszót"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Az eszköz újraindítását követően meg kell adni a mintát"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Az eszköz újraindítását követően meg kell adni a PIN-kódot"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Az eszköz újraindítását követően meg kell adni a jelszót"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hy/strings.xml b/packages/SystemUI/res-keyguard/values-hy/strings.xml
index 4224705..ad949d4 100644
--- a/packages/SystemUI/res-keyguard/values-hy/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hy/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Ծառայությունն անհասանելի է։"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Փոխել ներածման եղանակը"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Ավիառեժիմ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Թարմացմանը պատրաստվելու համար անհրաժեշտ է մուտքագրել PIN-ը"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Թարմացմանը պատրաստվելու համար անհրաժեշտ է մուտքագրել նախշը"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Թարմացմանը պատրաստվելու համար անհրաժեշտ է մուտքագրել գաղտնաբառը"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Սարքը վերագործարկելուց հետո անհրաժեշտ է մուտքագրել նախշը"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Սարքը վերագործարկելուց հետո անհրաժեշտ է մուտքագրել PIN կոդը"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Սարքը վերագործարկելուց հետո անհրաժեշտ է մուտքագրել գաղտնաբառը"</string>
diff --git a/packages/SystemUI/res-keyguard/values-in/strings.xml b/packages/SystemUI/res-keyguard/values-in/strings.xml
index d62795b..85b2a47 100644
--- a/packages/SystemUI/res-keyguard/values-in/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-in/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Tidak ada layanan."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Beralih metode masukan"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Mode pesawat"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN diwajibkan untuk menyiapkan update"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Pola diwajibkan untuk menyiapkan update"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Sandi diwajibkan untuk menyiapkan update"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pola diperlukan setelah perangkat dimulai ulang"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN diperlukan setelah perangkat dimulai ulang"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Sandi diperlukan setelah perangkat dimulai ulang"</string>
diff --git a/packages/SystemUI/res-keyguard/values-is/strings.xml b/packages/SystemUI/res-keyguard/values-is/strings.xml
index 5e37655..e40cdca 100644
--- a/packages/SystemUI/res-keyguard/values-is/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-is/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Ekkert símasamband."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Skipta um innsláttaraðferð"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Flugstilling"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Slá þarf inn PIN-númer til að undirbúa uppfærsluna"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Teikna þarf mynstur til að undirbúa uppfærsluna"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Gefa þarf upp aðgangsorð til að undirbúa uppfærsluna"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Mynsturs er krafist þegar tækið er endurræst"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN-númers er krafist þegar tækið er endurræst"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Aðgangsorðs er krafist þegar tækið er endurræst"</string>
diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml
index fe460e3..e1c9ee8 100644
--- a/packages/SystemUI/res-keyguard/values-it/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-it/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Nessun servizio."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Cambia metodo di immissione"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Modalità aereo"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN obbligatorio per la preparazione all\'aggiornamento"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Sequenza obbligatoria per la preparazione all\'aggiornamento"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Password obbligatoria per la preparazione all\'aggiornamento"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Sequenza obbligatoria dopo il riavvio del dispositivo"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN obbligatorio dopo il riavvio del dispositivo"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Password obbligatoria dopo il riavvio del dispositivo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-iw/strings.xml b/packages/SystemUI/res-keyguard/values-iw/strings.xml
index 71f3048..e054f62 100644
--- a/packages/SystemUI/res-keyguard/values-iw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-iw/strings.xml
@@ -107,9 +107,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"אין שירות."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"החלפת שיטת קלט"</string>
<string name="airplane_mode" msgid="2528005343938497866">"מצב טיסה"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"נדרש קוד אימות להכנת העדכון"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"נדרש קו ביטול נעילה להכנת העדכון"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"נדרשת סיסמה להכנת העדכון"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"יש להזין את קו ביטול הנעילה לאחר הפעלה מחדש של המכשיר"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"יש להזין קוד גישה לאחר הפעלה מחדש של המכשיר"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"יש להזין סיסמה לאחר הפעלה מחדש של המכשיר"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ja/strings.xml b/packages/SystemUI/res-keyguard/values-ja/strings.xml
index 23ff82c..957d78a 100644
--- a/packages/SystemUI/res-keyguard/values-ja/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ja/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"通信サービスはありません。"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"入力方法の切り替え"</string>
<string name="airplane_mode" msgid="2528005343938497866">"機内モード"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"更新の準備には PIN の入力が必要です"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"更新の準備にはパターンの入力が必要です"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"更新の準備にはパスワードが必要です"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"デバイスの再起動後はパターンの入力が必要となります"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"デバイスの再起動後は PIN の入力が必要となります"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"デバイスの再起動後はパスワードの入力が必要となります"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ka/strings.xml b/packages/SystemUI/res-keyguard/values-ka/strings.xml
index 25b9b1b..d0d15fe 100644
--- a/packages/SystemUI/res-keyguard/values-ka/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ka/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"სერვისი არ არის."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"შეყვანის მეთოდის გადართვა"</string>
<string name="airplane_mode" msgid="2528005343938497866">"თვითმფრინავის რეჟიმი"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"განახლების მოსამზადებლად საჭიროა PIN-კოდი"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"განახლების მოსამზადებლად საჭიროა ნიმუში"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"განახლების მოსამზადებლად საჭიროა პაროლი"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"მოწყობილობის გადატვირთვის შემდეგ საჭიროა ნიმუშის დახატვა"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"მოწყობილობის გადატვირთვის შემდეგ საჭიროა PIN-კოდის შეყვანა"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"მოწყობილობის გადატვირთვის შემდეგ საჭიროა პაროლის შეყვანა"</string>
diff --git a/packages/SystemUI/res-keyguard/values-kk/strings.xml b/packages/SystemUI/res-keyguard/values-kk/strings.xml
index 4560881..62afd1e 100644
--- a/packages/SystemUI/res-keyguard/values-kk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kk/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Қызмет көрсетілмейді."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Енгізу әдісін ауыстыру"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Ұшақ режимі"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Жаңа нұсқа орнатуға дайындау үшін PIN кодын енгізу қажет."</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Жаңа нұсқа орнатуға дайындау үшін өрнек енгізу қажет."</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Жаңа нұсқа орнатуға дайындау үшін құпия сөз енгізу қажет."</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Құрылғы қайта іске қосылғаннан кейін, өрнекті енгізу қажет"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Құрылғы қайта іске қосылғаннан кейін, PIN кодын енгізу қажет"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Құрылғы қайта іске қосылғаннан кейін, құпия сөзді енгізу қажет"</string>
diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml
index e5ea9ea..24b5c23 100644
--- a/packages/SystemUI/res-keyguard/values-km/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-km/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"គ្មានសេវាទេ។"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ប្ដូរវិធីបញ្ចូល"</string>
<string name="airplane_mode" msgid="2528005343938497866">"មុខងារពេលជិះយន្តហោះ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"តម្រូវឱ្យមានកូដ PIN ដើម្បីរៀបចំធ្វើបច្ចុប្បន្នភាព"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"តម្រូវឱ្យមានលំនាំ ដើម្បីរៀបចំធ្វើបច្ចុប្បន្នភាព"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"តម្រូវឱ្យមានពាក្យសម្ងាត់ ដើម្បីរៀបចំធ្វើបច្ចុប្បន្នភាព"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"តម្រូវឲ្យប្រើលំនាំ បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"តម្រូវឲ្យបញ្ចូលកូដ PIN បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"តម្រូវឲ្យបញ្ចូលពាក្យសម្ងាត់ បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml
index 8173ca0..785ca43 100644
--- a/packages/SystemUI/res-keyguard/values-kn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"ಸೇವೆ ಇಲ್ಲ."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ಇನ್ಪುಟ್ ವಿಧಾನ ಬದಲಿಸಿ"</string>
<string name="airplane_mode" msgid="2528005343938497866">"ಏರ್ಪ್ಲೇನ್ ಮೋಡ್"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"ಅಪ್ಡೇಟ್ಗಾಗಿ ಸಿದ್ಧಗೊಳಿಸಲು, ಪಿನ್ ಅಗತ್ಯವಿದೆ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"ಅಪ್ಡೇಟ್ಗಾಗಿ ಸಿದ್ಧಗೊಳಿಸಲು, ಪ್ಯಾಟರ್ನ್ ಅಗತ್ಯವಿದೆ"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"ಅಪ್ಡೇಟ್ಗಾಗಿ ಸಿದ್ಧಗೊಳಿಸಲು, ಪಾಸ್ವರ್ಡ್ ಅಗತ್ಯವಿದೆ"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ಸಾಧನ ಮರುಪ್ರಾರಂಭಗೊಂಡ ನಂತರ ಪ್ಯಾಟರ್ನ್ ಅಗತ್ಯವಿರುತ್ತದೆ"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ಸಾಧನ ಮರುಪ್ರಾರಂಭಗೊಂಡ ನಂತರ ಪಿನ್ ಅಗತ್ಯವಿರುತ್ತದೆ"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ಸಾಧನ ಮರುಪ್ರಾರಂಭಗೊಂಡ ನಂತರ ಪಾಸ್ವರ್ಡ್ ಅಗತ್ಯವಿರುತ್ತದೆ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml
index 606bb50..848490e 100644
--- a/packages/SystemUI/res-keyguard/values-ko/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"서비스 불가"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"입력 방법 전환"</string>
<string name="airplane_mode" msgid="2528005343938497866">"비행기 모드"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"업데이트를 준비하려면 PIN이 필요합니다."</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"업데이트를 준비하려면 패턴이 필요합니다."</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"업데이트를 준비하려면 비밀번호가 필요합니다."</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"기기가 다시 시작되면 패턴이 필요합니다."</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"기기가 다시 시작되면 PIN이 필요합니다."</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"기기가 다시 시작되면 비밀번호가 필요합니다."</string>
diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml
index 72f95db..d868788 100644
--- a/packages/SystemUI/res-keyguard/values-ky/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Интернет жок."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Киргизүү ыкмасын өзгөртүү"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Учак режими"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Жаңыртууга даярдоо үчүн PIN код талап кылынат"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Жаңыртууга даярдоо үчүн графикалык ачкыч талап кылынат"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Жаңыртууга даярдоо үчүн сырсөз талап кылынат"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Түзмөк кайра күйгүзүлгөндөн кийин графикалык ачкычты тартуу талап кылынат"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Түзмөк кайра күйгүзүлгөндөн кийин PIN-кодду киргизүү талап кылынат"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Түзмөк кайра күйгүзүлгөндөн кийин сырсөздү киргизүү талап кылынат"</string>
diff --git a/packages/SystemUI/res-keyguard/values-lo/strings.xml b/packages/SystemUI/res-keyguard/values-lo/strings.xml
index 25e36c1..ebaffb1 100644
--- a/packages/SystemUI/res-keyguard/values-lo/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lo/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"ບໍ່ມີບໍລິການ"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ສະລັບຮູບແບບການປ້ອນຂໍ້ມູນ"</string>
<string name="airplane_mode" msgid="2528005343938497866">"ໂໝດໃນຍົນ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"ຕ້ອງໃຊ້ PIN ເພື່ອກະກຽມອັບເດດ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"ຕ້ອງໃຊ້ຮູບແບບເພື່ອກະກຽມອັບເດດ"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"ຕ້ອງໃຊ້ລະຫັດຜ່ານເພື່ອກະກຽມອັບເດດ"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ຈຳເປັນຕ້ອງມີແບບຮູບປົດລັອກຫຼັງຈາກອຸປະກອນເລີ່ມລະບົບໃໝ່"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ຈຳເປັນຕ້ອງມີ PIN ຫຼັງຈາກອຸປະກອນເລີ່ມລະບົບໃໝ່"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ຈຳເປັນຕ້ອງມີລະຫັດຜ່ານຫຼັງຈາກອຸປະກອນເລີ່ມລະບົບໃໝ່"</string>
diff --git a/packages/SystemUI/res-keyguard/values-lt/strings.xml b/packages/SystemUI/res-keyguard/values-lt/strings.xml
index 158efe2..4d598f6 100644
--- a/packages/SystemUI/res-keyguard/values-lt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lt/strings.xml
@@ -107,9 +107,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Nėra paslaugos."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Perjungti įvesties metodą"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Lėktuvo režimas"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Kad būtų pasiruošta atnaujinti, reikia įvesti PIN kodą"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Kad būtų pasiruošta atnaujinti, reikia įvesti atrakinimo piešinį"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Kad būtų pasiruošta atnaujinti, reikia įvesti slaptažodį"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Iš naujo paleidus įrenginį būtinas atrakinimo piešinys"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Iš naujo paleidus įrenginį būtinas PIN kodas"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Iš naujo paleidus įrenginį būtinas slaptažodis"</string>
diff --git a/packages/SystemUI/res-keyguard/values-lv/strings.xml b/packages/SystemUI/res-keyguard/values-lv/strings.xml
index c4a4e7f..fad67d5 100644
--- a/packages/SystemUI/res-keyguard/values-lv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lv/strings.xml
@@ -104,9 +104,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Nav pakalpojuma."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Pārslēgt ievades metodi"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Lidojuma režīms"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Lai sagatavotos atjauninājumam, nepieciešams PIN."</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Lai sagatavotos atjauninājumam, nepieciešama kombinācija."</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Lai sagatavotos atjauninājumam, nepieciešama parole."</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Pēc ierīces restartēšanas ir jāievada atbloķēšanas kombinācija."</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Pēc ierīces restartēšanas ir jāievada PIN kods."</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Pēc ierīces restartēšanas ir jāievada parole."</string>
diff --git a/packages/SystemUI/res-keyguard/values-mk/strings.xml b/packages/SystemUI/res-keyguard/values-mk/strings.xml
index de4a83b..1397f46 100644
--- a/packages/SystemUI/res-keyguard/values-mk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mk/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Нема услуга."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Префрли метод за внесување"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Авионски режим"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Потребен е PIN за да се подготви за ажурирање"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Потребна е шема за да се подготви за ажурирање"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Потребна е лозинка за да се подготви за ажурирање"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Потребна е шема по рестартирање на уредот"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Потребен е PIN-код по рестартирање на уредот"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Потребна е лозинка по рестартирање на уредот"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ml/strings.xml b/packages/SystemUI/res-keyguard/values-ml/strings.xml
index da26ba7..f82f822 100644
--- a/packages/SystemUI/res-keyguard/values-ml/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ml/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"സേവനമില്ല"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ഇൻപുട്ട് രീതി മാറുക"</string>
<string name="airplane_mode" msgid="2528005343938497866">"ഫ്ലൈറ്റ് മോഡ്"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"അപ്ഡേറ്റിനായി തയ്യാറെടുക്കാൻ പിൻ ആവശ്യമാണ്"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"അപ്ഡേറ്റിനായി തയ്യാറെടുക്കാൻ പാറ്റേൺ ആവശ്യമാണ്"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"അപ്ഡേറ്റിനായി തയ്യാറെടുക്കാൻ പാസ്വേഡ് ആവശ്യമാണ്"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ഉപകരണം റീസ്റ്റാർട്ടായശേഷം പാറ്റേൺ വരയ്ക്കേണ്ടതുണ്ട്"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ഉപകരണം റീസ്റ്റാർട്ടായശേഷം പിൻ നൽകേണ്ടതുണ്ട്"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ഉപകരണം റീസ്റ്റാർട്ടായശേഷം പാസ്വേഡ് നൽകേണ്ടതുണ്ട്"</string>
diff --git a/packages/SystemUI/res-keyguard/values-mn/strings.xml b/packages/SystemUI/res-keyguard/values-mn/strings.xml
index fb032f1..462017a 100644
--- a/packages/SystemUI/res-keyguard/values-mn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mn/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Үйлчилгээ алга."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Оруулах аргыг сэлгэх"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Нислэгийн горим"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Шинэчлэхэд бэлтгэхийн тулд ПИН шаардлагатай"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Шинэчлэхэд бэлтгэхийн тулд хээ шаардлагатай"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Шинэчлэхэд бэлтгэхийн тулд нууц үг шаардлагатай"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Төхөөрөмжийг дахин эхлүүлсний дараа загвар оруулах шаардлагатай"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Төхөөрөмжийг дахин эхлүүлсний дараа ПИН оруулах шаардлагатай"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Төхөөрөмжийг дахин эхлүүлсний дараа нууц үг оруулах шаардлагатай"</string>
diff --git a/packages/SystemUI/res-keyguard/values-mr/strings.xml b/packages/SystemUI/res-keyguard/values-mr/strings.xml
index e79e5c5..0166791 100644
--- a/packages/SystemUI/res-keyguard/values-mr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mr/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"सेवा नाही."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"इनपुट पद्धत स्विच करा"</string>
<string name="airplane_mode" msgid="2528005343938497866">"विमान मोड"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"अपडेटसाठी तयार करण्याकरिता पिन आवश्यक आहे"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"अपडेटसाठी तयार करण्याकरिता पॅटर्न आवश्यक आहे"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"अपडेटसाठी तयार करण्याकरिता पासवर्ड आवश्यक आहे"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"डिव्हाइस रीस्टार्ट झाल्यावर पॅटर्न आवश्यक आहे"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"डिव्हाइस रीस्टार्ट झाल्यावर पिन आवश्यक आहे"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"डिव्हाइस रीस्टार्ट झाल्यावर पासवर्ड आवश्यक आहे"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ms/strings.xml b/packages/SystemUI/res-keyguard/values-ms/strings.xml
index 5bc5df4..6750086 100644
--- a/packages/SystemUI/res-keyguard/values-ms/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ms/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Tiada perkhidmatan."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Tukar kaedah masukan"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Mod Pesawat"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN diperlukan untuk menyediakan kemas kini"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Corak diperlukan untuk menyediakan kemas kini"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Kata laluan diperlukan untuk menyediakan kemas kini"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Corak diperlukan setelah peranti dimulakan semula"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN diperlukan setelah peranti dimulakan semula"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Kata laluan diperlukan setelah peranti dimulakan semula"</string>
diff --git a/packages/SystemUI/res-keyguard/values-my/strings.xml b/packages/SystemUI/res-keyguard/values-my/strings.xml
index 43732d8..3b32f06 100644
--- a/packages/SystemUI/res-keyguard/values-my/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-my/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"ဝန်ဆောင်မှု မရှိပါ။"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"စာရိုက်စနစ်ပြောင်းရန်"</string>
<string name="airplane_mode" msgid="2528005343938497866">"လေယာဉ်ပျံမုဒ်"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"အပ်ဒိတ်အတွက် ပြင်ဆင်ရန် ပင်နံပါတ် လိုပါသည်"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"အပ်ဒိတ်အတွက် ပြင်ဆင်ရန် ပုံစံလိုပါသည်"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"အပ်ဒိတ်အတွက် ပြင်ဆင်ရန် စကားဝှက် လိုပါသည်"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"စက်ပစ္စည်းကို ပိတ်ပြီးပြန်ဖွင့်လိုက်သည့်အခါတွင် ပုံစံ လိုအပ်ပါသည်"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"စက်ပစ္စည်းကို ပိတ်ပြီးပြန်ဖွင့်လိုက်သည့်အခါတွင် ပင်နံပါတ် လိုအပ်ပါသည်"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"စက်ပစ္စည်းကို ပိတ်ပြီးပြန်ဖွင့်လိုက်သည့်အခါတွင် စကားဝှက် လိုအပ်ပါသည်"</string>
diff --git a/packages/SystemUI/res-keyguard/values-nb/strings.xml b/packages/SystemUI/res-keyguard/values-nb/strings.xml
index 6dd3b2a..ebd8f29 100644
--- a/packages/SystemUI/res-keyguard/values-nb/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nb/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Ingen tilkobling."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Bytt inndatametode"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Flymodus"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN-koden kreves for å klargjøre for oppdateringen"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Mønsteret kreves for å klargjøre for oppdateringen"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Passordet kreves for å klargjøre for oppdateringen"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Du må tegne mønsteret etter at enheten har startet på nytt"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Du må skrive inn PIN-koden etter at enheten har startet på nytt"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Du må skrive inn passordet etter at enheten har startet på nytt"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml
index a1e6d62..ce05e38 100644
--- a/packages/SystemUI/res-keyguard/values-ne/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"सेवा उपलब्ध छैन।"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"इनपुट विधिलाई स्विच गर्नुहोस्"</string>
<string name="airplane_mode" msgid="2528005343938497866">"हवाइजहाज मोड"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"अद्यावधिक गर्ने कार्यका लागि तयार पार्न PIN चाहिन्छ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"अद्यावधिक गर्ने कार्यका लागि तयार पार्न प्याटर्न चाहिन्छ"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"अद्यावधिक गर्ने कार्यका लागि तयार पार्न पासवर्ड चाहिन्छ"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"यन्त्र पुनः सुरु भएपछि ढाँचा आवश्यक पर्दछ"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"यन्त्र पुनः सुरु भएपछि PIN आवश्यक पर्दछ"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"यन्त्र पुनः सुरु भएपछि पासवर्ड आवश्यक पर्दछ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-nl/strings.xml b/packages/SystemUI/res-keyguard/values-nl/strings.xml
index f3c35a4..aa783e8 100644
--- a/packages/SystemUI/res-keyguard/values-nl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nl/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Geen service."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Invoermethode wijzigen"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Vliegtuigmodus"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Pincode vereist voor voorbereiding op update"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Patroon vereist voor voorbereiding op update"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Wachtwoord vereist voor voorbereiding op update"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Patroon vereist nadat het apparaat opnieuw is opgestart"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Pincode vereist nadat het apparaat opnieuw is opgestart"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Wachtwoord vereist nadat het apparaat opnieuw is opgestart"</string>
diff --git a/packages/SystemUI/res-keyguard/values-or/strings.xml b/packages/SystemUI/res-keyguard/values-or/strings.xml
index e92dc42..8bbdcf1 100644
--- a/packages/SystemUI/res-keyguard/values-or/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-or/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"କୌଣସି ସେବା ନାହିଁ।"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ଇନପୁଟ୍ ପଦ୍ଧତି ବଦଳାନ୍ତୁ"</string>
<string name="airplane_mode" msgid="2528005343938497866">"ଏରୋପ୍ଲେନ୍ ମୋଡ୍"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"ଅପଡେଟ୍ ପାଇଁ ପ୍ରସ୍ତୁତ ହେବାକୁ PIN ଆବଶ୍ୟକ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"ଅପଡେଟ୍ ପାଇଁ ପ୍ରସ୍ତୁତ ହେବାକୁ ପାଟର୍ନ ଆବଶ୍ୟକ"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"ଅପଡେଟ୍ ପାଇଁ ପ୍ରସ୍ତୁତ ହେବାକୁ ପାସୱାର୍ଡ ଆବଶ୍ୟକ"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ଡିଭାଇସ୍ ରିଷ୍ଟାର୍ଟ ହେବା ପରେ ପାଟର୍ନ ଆବଶ୍ୟକ ଅଟେ"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ଡିଭାଇସ୍ ରିଷ୍ଟାର୍ଟ ହେବାପରେ ପାସ୍ୱର୍ଡ ଆବଶ୍ୟକ"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ଡିଭାଇସ୍ ରିଷ୍ଟାର୍ଟ ହେବା ପରେ ପାସୱର୍ଡ ଆବଶ୍ୟକ ଅଟେ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml
index 5c83ab8..78e0665 100644
--- a/packages/SystemUI/res-keyguard/values-pa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"ਕੋਈ ਸੇਵਾ ਨਹੀਂ।"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ਇਨਪੁੱਟ ਵਿਧੀ ਸਵਿੱਚ ਕਰੋ"</string>
<string name="airplane_mode" msgid="2528005343938497866">"ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"ਅੱਪਡੇਟ ਨੂੰ ਤਿਆਰ ਕਰਨ ਲਈ ਪਿੰਨ ਲੋੜੀਂਦਾ ਹੈ"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"ਅੱਪਡੇਟ ਨੂੰ ਤਿਆਰ ਕਰਨ ਲਈ ਪੈਟਰਨ ਲੋੜੀਂਦਾ ਹੈ"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"ਅੱਪਡੇਟ ਨੂੰ ਤਿਆਰ ਕਰਨ ਲਈ ਪਾਸਵਰਡ ਲੋੜੀਂਦਾ ਹੈ"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ਡੀਵਾਈਸ ਦੇ ਮੁੜ-ਚਾਲੂ ਹੋਣ \'ਤੇ ਪੈਟਰਨ ਦੀ ਲੋੜ ਹੈ"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ਡੀਵਾਈਸ ਦੇ ਮੁੜ-ਚਾਲੂ ਹੋਣ \'ਤੇ ਪਿੰਨ ਦੀ ਲੋੜ ਹੈ"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ਡੀਵਾਈਸ ਦੇ ਮੁੜ-ਚਾਲੂ ਹੋਣ \'ਤੇ ਪਾਸਵਰਡ ਦੀ ਲੋੜ ਹੈ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml
index 5f1df3e..5094cf9 100644
--- a/packages/SystemUI/res-keyguard/values-pl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml
@@ -107,9 +107,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Brak usługi."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Przełączanie metody wprowadzania"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Tryb samolotowy"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Aby przygotować się do aktualizacji, wymagany jest kod PIN"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Aby przygotować się do aktualizacji, wymagany jest wzór"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Aby przygotować się do aktualizacji, wymagane jest hasło"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Po ponownym uruchomieniu urządzenia wymagany jest wzór"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Po ponownym uruchomieniu urządzenia wymagany jest kod PIN"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Po ponownym uruchomieniu urządzenia wymagane jest hasło"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
index 1e47efa..5bfc3db 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Sem serviço."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Alterar o método de entrada"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Modo avião"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Insira o PIN para se preparar para a atualização"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Insira o padrão para se preparar para a atualização"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Insira a senha para se preparar para a atualização"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"O padrão é exigido após a reinicialização do dispositivo"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"O PIN é exigido após a reinicialização do dispositivo"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"A senha é exigida após a reinicialização do dispositivo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
index 09cfcf1..5af8bc0 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Sem serviço."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Alternar o método de introdução"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Modo de avião"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"É necessário introduzir o PIN para a preparação para a atualização."</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"É necessário introduzir o padrão para a preparação para a atualização."</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"É necessário introduzir a palavra-passe para a preparação para a atualização."</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"É necessário um padrão após reiniciar o dispositivo"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"É necessário um PIN após reiniciar o dispositivo"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"É necessária uma palavra-passe após reiniciar o dispositivo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt/strings.xml b/packages/SystemUI/res-keyguard/values-pt/strings.xml
index 1e47efa..5bfc3db 100644
--- a/packages/SystemUI/res-keyguard/values-pt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Sem serviço."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Alterar o método de entrada"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Modo avião"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Insira o PIN para se preparar para a atualização"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Insira o padrão para se preparar para a atualização"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Insira a senha para se preparar para a atualização"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"O padrão é exigido após a reinicialização do dispositivo"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"O PIN é exigido após a reinicialização do dispositivo"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"A senha é exigida após a reinicialização do dispositivo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml
index 7df2db6..8122241 100644
--- a/packages/SystemUI/res-keyguard/values-ro/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml
@@ -104,9 +104,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Fără serviciu."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Comutați metoda de introducere"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Mod Avion"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Pentru a vă pregăti pentru actualizare este necesar codul PIN"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Pentru a vă pregăti pentru actualizare este necesar modelul"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Pentru a vă pregăti pentru actualizare este necesară parola"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Modelul este necesar după repornirea dispozitivului"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Codul PIN este necesar după repornirea dispozitivului"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Parola este necesară după repornirea dispozitivului"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml
index ccd3c96..b80b479 100644
--- a/packages/SystemUI/res-keyguard/values-ru/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml
@@ -107,9 +107,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Нет сигнала."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Сменить способ ввода"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Режим полета"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Для подготовки к обновлению необходимо ввести PIN-код."</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Для подготовки к обновлению необходимо ввести графический ключ."</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Для подготовки к обновлению необходимо ввести пароль."</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"После перезагрузки устройства необходимо ввести графический ключ"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"После перезагрузки устройства необходимо ввести PIN-код"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"После перезагрузки устройства необходимо ввести пароль"</string>
diff --git a/packages/SystemUI/res-keyguard/values-si/strings.xml b/packages/SystemUI/res-keyguard/values-si/strings.xml
index 3dfc282..1cd876f 100644
--- a/packages/SystemUI/res-keyguard/values-si/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-si/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"සේවාව නැත."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ආදාන ක්රමය මාරු කිරීම"</string>
<string name="airplane_mode" msgid="2528005343938497866">"ගුවන් යානා ප්රකාරය"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"යාවත්කාලීනය සඳහා සුදානම් කිරීමට PIN අවශ්යය"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"යාවත්කාලීනය සඳහා සුදානම් කිරීමට රටාව අවශ්යය"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"යාවත්කාලීනය සඳහා සුදානම් කිරීමට මුරපදය අවශ්යය"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"උපාංගය නැවත ආරම්භ වූ පසු රටාව අවශ්යයි"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"උපාංගය නැවත ආරම්භ වූ පසු PIN අංකය අවශ්යයි"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"උපාංගය නැවත ආරම්භ වූ පසු මුරපදය අවශ්යයි"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sk/strings.xml b/packages/SystemUI/res-keyguard/values-sk/strings.xml
index 8568e14..801a7db 100644
--- a/packages/SystemUI/res-keyguard/values-sk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sk/strings.xml
@@ -107,9 +107,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Žiadny signál."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Prepnúť metódu vstupu"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Režim v lietadle"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Príprava na aktualizáciu vyžaduje zadanie kódu PIN"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Príprava na aktualizáciu vyžaduje zadanie vzoru"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Príprava na aktualizáciu vyžaduje zadanie hesla"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Po reštartovaní zariadenia musíte zadať bezpečnostný vzor"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Po reštartovaní zariadenia musíte zadať kód PIN"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Po reštartovaní zariadenia musíte zadať heslo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sl/strings.xml b/packages/SystemUI/res-keyguard/values-sl/strings.xml
index 168158d..967255c 100644
--- a/packages/SystemUI/res-keyguard/values-sl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sl/strings.xml
@@ -107,9 +107,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Ni storitve."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Preklop načina vnosa"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Način za letalo"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Za pripravo na posodobitev morate vnesti kodo PIN"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Za pripravo na posodobitev morate vnesti vzorec"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Za pripravo na posodobitev morate vnesti geslo"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Po vnovičnem zagonu naprave je treba vnesti vzorec"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Po vnovičnem zagonu naprave je treba vnesti kodo PIN"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Po vnovičnem zagonu naprave je treba vnesti geslo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sq/strings.xml b/packages/SystemUI/res-keyguard/values-sq/strings.xml
index 14973f8..382a4dc 100644
--- a/packages/SystemUI/res-keyguard/values-sq/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sq/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Nuk ka shërbim."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Ndërro metodën e hyrjes"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Modaliteti i aeroplanit"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Kërkohet kodi PIN për t\'u përgatitur për përditësimin"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Kërkohet motivi për t\'u përgatitur për përditësimin"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Kërkohet fjalëkalimi për t\'u përgatitur për përditësimin"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Kërkohet motivi pas rinisjes së pajisjes"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Kërkohet kodi PIN pas rinisjes së pajisjes"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Kërkohet fjalëkalimi pas rinisjes së pajisjes"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sr/strings.xml b/packages/SystemUI/res-keyguard/values-sr/strings.xml
index 24a1125..f83df3f 100644
--- a/packages/SystemUI/res-keyguard/values-sr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sr/strings.xml
@@ -104,9 +104,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Мрежа није доступна."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Промени метод уноса"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Режим рада у авиону"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"PIN је обавезан ради припреме за ажурирање"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Шаблон је обавезан ради припреме за ажурирање"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Лозинка је обавезна ради припреме за ажурирање"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Треба да унесете шаблон када се уређај поново покрене"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Треба да унесете PIN када се уређај поново покрене"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Треба да унесете лозинку када се уређај поново покрене"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sv/strings.xml b/packages/SystemUI/res-keyguard/values-sv/strings.xml
index a37c480..a037bff 100644
--- a/packages/SystemUI/res-keyguard/values-sv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sv/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Ingen tjänst."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Byt inmatningsmetod"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Flygplansläge"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Pinkod krävs för att förbereda för uppdatering"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Grafiskt lösenord krävs för att förbereda för uppdatering"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Lösenord krävs för att förbereda för uppdatering"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Du måste ange grafiskt lösenord när du har startat om enheten"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Du måste ange pinkod när du har startat om enheten"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Du måste ange lösenord när du har startat om enheten"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sw/strings.xml b/packages/SystemUI/res-keyguard/values-sw/strings.xml
index c4a9a14..efa5ecf 100644
--- a/packages/SystemUI/res-keyguard/values-sw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sw/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Hakuna mtandao."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Kubadili mbinu ya kuingiza data"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Hali ya ndegeni"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Inahitaji PIN ili kujiandaa kwa ajili ya sasisho"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Inahitaji mchoro ili kujiandaa kwa ajili ya sasisho"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Inahitaji nenosiri ili kujiandaa kwa ajili ya sasisho"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Unafaa kuchora mchoro baada ya kuwasha kifaa upya"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Unafaa kuweka PIN baada ya kuwasha kifaa upya"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Unafaa kuweka nenosiri baada ya kuwasha kifaa upya"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml
index a4dc0be..96dbbb0 100644
--- a/packages/SystemUI/res-keyguard/values-ta/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"சேவை இல்லை."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"உள்ளீட்டு முறையை மாற்றும்"</string>
<string name="airplane_mode" msgid="2528005343938497866">"விமானப் பயன்முறை"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"புதுப்பிப்பிற்குத் தயார்செய்ய பின் தேவை"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"புதுப்பிப்பிற்குத் தயார்செய்ய பேட்டர்ன் தேவை"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"புதுப்பிப்பிற்குத் தயார்செய்ய கடவுச்சொல் தேவை"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"சாதனத்தை மீண்டும் தொடங்கியதும், பேட்டர்னை வரைய வேண்டும்"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"சாதனத்தை மீண்டும் தொடங்கியதும், பின்னை உள்ளிட வேண்டும்"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"சாதனத்தை மீண்டும் தொடங்கியதும், கடவுச்சொல்லை உள்ளிட வேண்டும்"</string>
diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml
index 3e27ce8..74386bc 100644
--- a/packages/SystemUI/res-keyguard/values-te/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-te/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"సేవ లేదు."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ఇన్పుట్ పద్ధతిని మార్చు"</string>
<string name="airplane_mode" msgid="2528005343938497866">"విమానం మోడ్"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"అప్డేట్కు సిద్ధం చేయడానికి పిన్ అవసరం"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"అప్డేట్కు సిద్ధం చేయడానికి ఆకృతి అవసరం"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"అప్డేట్కు సిద్ధం చేయడానికి పాస్వర్డ్ అవసరం"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"పరికరాన్ని పునఃప్రారంభించిన తర్వాత నమూనాను గీయాలి"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"డివైజ్ను పునఃప్రారంభించిన తర్వాత పిన్ నమోదు చేయాలి"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"పరికరాన్ని పునఃప్రారంభించిన తర్వాత పాస్వర్డ్ను నమోదు చేయాలి"</string>
diff --git a/packages/SystemUI/res-keyguard/values-th/strings.xml b/packages/SystemUI/res-keyguard/values-th/strings.xml
index 48ae2ce..e157be4 100644
--- a/packages/SystemUI/res-keyguard/values-th/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-th/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"ไม่มีบริการ"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"สลับวิธีการป้อนข้อมูล"</string>
<string name="airplane_mode" msgid="2528005343938497866">"โหมดบนเครื่องบิน"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"ต้องใช้ PIN เพื่อเตรียมรับการอัปเดต"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"ต้องใช้รูปแบบเพื่อเตรียมรับการอัปเดต"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"ต้องใช้รหัสผ่านเพื่อเตรียมรับการอัปเดต"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"ต้องวาดรูปแบบหลังจากอุปกรณ์รีสตาร์ท"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"ต้องระบุ PIN หลังจากอุปกรณ์รีสตาร์ท"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"ต้องป้อนรหัสผ่านหลังจากอุปกรณ์รีสตาร์ท"</string>
diff --git a/packages/SystemUI/res-keyguard/values-tl/strings.xml b/packages/SystemUI/res-keyguard/values-tl/strings.xml
index bd87b20..7b7e17d 100644
--- a/packages/SystemUI/res-keyguard/values-tl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tl/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Walang serbisyo."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Magpalit ng pamamaraan ng pag-input"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Airplane mode"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Kinakailangan ang PIN para makapaghanda sa pag-update"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Kinakailangan ang pattern para makapaghanda sa pag-update"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Kinakailangan ang password para makapaghanda sa pag-update"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Kailangan ng pattern pagkatapos mag-restart ng device"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Kailangan ng PIN pagkatapos mag-restart ng device"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Kailangan ng password pagkatapos mag-restart ng device"</string>
diff --git a/packages/SystemUI/res-keyguard/values-tr/strings.xml b/packages/SystemUI/res-keyguard/values-tr/strings.xml
index cf37451..8c0caea 100644
--- a/packages/SystemUI/res-keyguard/values-tr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tr/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Hizmet yok."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Giriş yöntemini değiştir"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Uçak modu"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Güncellemenin hazırlanması için PIN gerekli"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Güncellemenin hazırlanması için desen gerekli"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Güncellemenin hazırlanması için şifre gerekli"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Cihaz yeniden başladıktan sonra desen gerekir"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Cihaz yeniden başladıktan sonra PIN gerekir"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Cihaz yeniden başladıktan sonra şifre gerekir"</string>
diff --git a/packages/SystemUI/res-keyguard/values-uk/strings.xml b/packages/SystemUI/res-keyguard/values-uk/strings.xml
index 0ccd012..6e5ce0f 100644
--- a/packages/SystemUI/res-keyguard/values-uk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uk/strings.xml
@@ -107,9 +107,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Зв’язку немає."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Змінити метод введення"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Режим польоту"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Щоб підготуватися до оновлення, введіть PIN-код"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Щоб підготуватися до оновлення, введіть ключ"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Щоб підготуватися до оновлення, введіть пароль"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Після перезавантаження пристрою потрібно ввести ключ"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Після перезавантаження пристрою потрібно ввести PIN-код"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Після перезавантаження пристрою потрібно ввести пароль"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml
index 22a477e..0fd5e17 100644
--- a/packages/SystemUI/res-keyguard/values-ur/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"کوئی سروس نہیں ہے۔"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"اندراج کا طریقہ سوئچ کریں"</string>
<string name="airplane_mode" msgid="2528005343938497866">"ہوائی جہاز وضع"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"اپ ڈیٹ تیار کرنے کے لیے PIN درکار ہے"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"اپ ڈیٹ تیار کرنے کے لیے پیٹرن درکار ہے"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"اپ ڈیٹ تیار کرنے کے لیے پاس ورڈ درکار ہے"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"آلہ دوبارہ چالو ہونے کے بعد پیٹرن درکار ہوتا ہے"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"آلہ دوبارہ چالو ہونے کے بعد PIN درکار ہوتا ہے"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"آلہ دوبارہ چالو ہونے کے بعد پاسورڈ درکار ہوتا ہے"</string>
diff --git a/packages/SystemUI/res-keyguard/values-uz/strings.xml b/packages/SystemUI/res-keyguard/values-uz/strings.xml
index 72d4fae..323fea5 100644
--- a/packages/SystemUI/res-keyguard/values-uz/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uz/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Aloqa yo‘q."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Matn kiritish usulini almashtirish"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Parvoz rejimi"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Yangilashga tayyorlash uchun PIN kod talab etiladi"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Yangilashga tayyorlash uchun grafik kalit talab etiladi"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Yangilashga tayyorlash uchun parol talab etiladi"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Qurilma o‘chirib yoqilgandan keyin grafik kalit talab qilinadi"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Qurilma o‘chirib yoqilgandan keyin PIN kod talab qilinadi"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Qurilma o‘chirib yoqilgandan keyin parol talab qilinadi"</string>
diff --git a/packages/SystemUI/res-keyguard/values-vi/strings.xml b/packages/SystemUI/res-keyguard/values-vi/strings.xml
index de642f3..2ba5089 100644
--- a/packages/SystemUI/res-keyguard/values-vi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-vi/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Không có dịch vụ."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Chuyển phương thức nhập"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Chế độ trên máy bay"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Cần nhập mã PIN để chuẩn bị cập nhật"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Cần nhập hình mở khóa để chuẩn bị cập nhật"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Cần nhập mật khẩu để chuẩn bị cập nhật"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Yêu cầu hình mở khóa sau khi thiết bị khởi động lại"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Yêu cầu mã PIN sau khi thiết bị khởi động lại"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Yêu cầu mật khẩu sau khi thiết bị khởi động lại"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
index be162b0..b4bff5f 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"无服务。"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"切换输入法"</string>
<string name="airplane_mode" msgid="2528005343938497866">"飞行模式"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"需要输入 PIN 码才能让设备做好更新准备"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"需要绘制解锁图案才能让设备做好更新准备"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"需要输入密码才能让设备做好更新准备"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"重启设备后需要绘制解锁图案"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"重启设备后需要输入 PIN 码"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"重启设备后需要输入密码"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
index 33e5b44..b3d3877 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"沒有服務。"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"轉換輸入方法"</string>
<string name="airplane_mode" msgid="2528005343938497866">"飛行模式"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"必須提供 PIN 碼,才能準備進行更新"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"必須畫出上鎖圖案,才能準備進行更新"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"必須輸入密碼,才能準備進行更新"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"裝置重新啟動後,必須畫出上鎖圖案才能使用"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"裝置重新啟動後,必須輸入 PIN 碼才能使用"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"裝置重新啟動後,必須輸入密碼才能使用"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
index 763233c..03dec48 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"沒有服務。"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"切換輸入法"</string>
<string name="airplane_mode" msgid="2528005343938497866">"飛航模式"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"請輸入 PIN 碼,以便為更新作業進行準備"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"請畫出解鎖圖案,以便為更新作業進行準備"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"請輸入密碼,以便為更新作業進行準備"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"裝置重新啟動後需要畫出解鎖圖案"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"裝置重新啟動後需要輸入 PIN 碼"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"裝置重新啟動後需要輸入密碼"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zu/strings.xml b/packages/SystemUI/res-keyguard/values-zu/strings.xml
index 397c868..5ab567f 100644
--- a/packages/SystemUI/res-keyguard/values-zu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zu/strings.xml
@@ -101,9 +101,6 @@
<string name="keyguard_carrier_default" msgid="6359808469637388586">"Ayikho isevisi"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Shintsha indlela yokufaka"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Imodi yendiza"</string>
- <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Iphinikhodi iyadingeka ukuze kulungiselelwe isibuyekezo"</string>
- <string name="kg_prompt_reason_prepare_for_update_pattern" msgid="4873394344883271519">"Iphethini iyadingeka ukuze kulungiselelwe isibuyekezo"</string>
- <string name="kg_prompt_reason_prepare_for_update_password" msgid="5625446803865598337">"Iphasiwedi iyadingeka ukuze kulungiselelwe isibuyekezo"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Iphethini iyadingeka ngemuva kokuqala kabusha kwedivayisi"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Iphinikhodi iyadingeka ngemuva kokuqala kabusha kwedivayisi"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Iphasiwedi iyadingeka ngemuva kokuqala kabusha kwedivayisi"</string>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 4d184d5..f7e9fed 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -240,15 +240,6 @@
<!-- Description of airplane mode -->
<string name="airplane_mode">Airplane mode</string>
- <!-- An explanation text that the PIN needs to be entered to prepare for an operating system update. [CHAR LIMIT=80] -->
- <string name="kg_prompt_reason_prepare_for_update_pin">PIN required to prepare for update</string>
-
- <!-- An explanation text that the pattern needs to be entered to prepare for an operating system update. [CHAR LIMIT=80] -->
- <string name="kg_prompt_reason_prepare_for_update_pattern">Pattern required to prepare for update</string>
-
- <!-- An explanation text that the password needs to be entered to prepare for an operating system update. [CHAR LIMIT=80] -->
- <string name="kg_prompt_reason_prepare_for_update_password">Password required to prepare for update</string>
-
<!-- An explanation text that the pattern needs to be solved since the device has just been restarted. [CHAR LIMIT=80] -->
<string name="kg_prompt_reason_restart_pattern">Pattern required after device restarts</string>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 5f2a946..53eb234 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -23,10 +23,12 @@
<item name="android:textColor">?attr/wallpaperTextColorSecondary</item>
<item name="android:textSize">@dimen/kg_status_line_font_size</item>
</style>
- <style name="Keyguard.TextView.EmergencyButton" parent="@android:style/DeviceDefault.ButtonBar">
+ <style name="Keyguard.TextView.EmergencyButton" parent="Theme.SystemUI">
<item name="android:textColor">?attr/wallpaperTextColorSecondary</item>
- <item name="android:textSize">@dimen/kg_status_line_font_size</item>
- <item name="android:background">@null</item>
+ <item name="android:textSize">14dp</item>
+ <item name="android:background">@drawable/kg_emergency_button_background</item>
+ <item name="android:paddingLeft">12dp</item>
+ <item name="android:paddingRight">12dp</item>
</style>
<style name="Widget.TextView.NumPadKey" parent="@android:style/Widget.TextView">
<item name="android:singleLine">true</item>
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button_ccw_start_0.xml
similarity index 84%
copy from packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml
copy to packages/SystemUI/res/drawable/ic_sysbar_rotate_button_ccw_start_0.xml
index 3304c19..ff5cb9e 100644
--- a/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml
+++ b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button_ccw_start_0.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2017 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.
@@ -23,7 +23,7 @@
android:viewportHeight="28.0">
<!-- Use scaleX to flip icon so arrows always point in the direction of motion -->
<group android:name="icon" android:pivotX="14" android:pivotY="14"
- android:scaleX="?attr/rotateButtonScaleX">
+ android:scaleX="1">
<!-- Tint color to be set directly -->
<path android:fillColor="#FFFFFFFF"
android:pathData="M12.02,10.83L9.25,8.06l2.77,-2.77l1.12,1.12l-0.85,0.86h5.16c0.72,0 1.31,0.56 1.31,1.26v9.16l-1.58,-1.58V8.85h-4.89l0.86,0.86L12.02,10.83zM15.98,17.17l-1.12,1.12l0.85,0.86h-4.88v-7.26L9.25,10.3v9.17c0,0.7 0.59,1.26 1.31,1.26h5.16v0.01l-0.85,0.85l1.12,1.12l2.77,-2.77L15.98,17.17z"/>
@@ -107,8 +107,8 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="100"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="0"
+ android:valueTo="-90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -118,14 +118,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonStartAngle"/>
+ android:valueFrom="0"
+ android:valueTo="0"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="0"
+ android:valueTo="-90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -135,14 +135,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonStartAngle"/>
+ android:valueFrom="0"
+ android:valueTo="0"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="0"
+ android:valueTo="-90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -152,14 +152,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonStartAngle"/>
+ android:valueFrom="0"
+ android:valueTo="0"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="0"
+ android:valueTo="-90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -169,14 +169,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonStartAngle"/>
+ android:valueFrom="0"
+ android:valueTo="0"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="0"
+ android:valueTo="-90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button_ccw_start_90.xml
similarity index 84%
copy from packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml
copy to packages/SystemUI/res/drawable/ic_sysbar_rotate_button_ccw_start_90.xml
index 3304c19..90fedb1 100644
--- a/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml
+++ b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button_ccw_start_90.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2017 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.
@@ -23,7 +23,7 @@
android:viewportHeight="28.0">
<!-- Use scaleX to flip icon so arrows always point in the direction of motion -->
<group android:name="icon" android:pivotX="14" android:pivotY="14"
- android:scaleX="?attr/rotateButtonScaleX">
+ android:scaleX="1">
<!-- Tint color to be set directly -->
<path android:fillColor="#FFFFFFFF"
android:pathData="M12.02,10.83L9.25,8.06l2.77,-2.77l1.12,1.12l-0.85,0.86h5.16c0.72,0 1.31,0.56 1.31,1.26v9.16l-1.58,-1.58V8.85h-4.89l0.86,0.86L12.02,10.83zM15.98,17.17l-1.12,1.12l0.85,0.86h-4.88v-7.26L9.25,10.3v9.17c0,0.7 0.59,1.26 1.31,1.26h5.16v0.01l-0.85,0.85l1.12,1.12l2.77,-2.77L15.98,17.17z"/>
@@ -107,8 +107,8 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="100"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="90"
+ android:valueTo="0">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -118,14 +118,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonStartAngle"/>
+ android:valueFrom="90"
+ android:valueTo="90"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="90"
+ android:valueTo="0">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -135,14 +135,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonStartAngle"/>
+ android:valueFrom="90"
+ android:valueTo="90"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="90"
+ android:valueTo="0">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -152,14 +152,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonStartAngle"/>
+ android:valueFrom="90"
+ android:valueTo="90"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="90"
+ android:valueTo="0">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -169,14 +169,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonStartAngle"/>
+ android:valueFrom="90"
+ android:valueTo="90"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="90"
+ android:valueTo="0">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button_cw_start_0.xml
similarity index 84%
rename from packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml
rename to packages/SystemUI/res/drawable/ic_sysbar_rotate_button_cw_start_0.xml
index 3304c19..a89e7a3 100644
--- a/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml
+++ b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button_cw_start_0.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2017 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.
@@ -23,7 +23,7 @@
android:viewportHeight="28.0">
<!-- Use scaleX to flip icon so arrows always point in the direction of motion -->
<group android:name="icon" android:pivotX="14" android:pivotY="14"
- android:scaleX="?attr/rotateButtonScaleX">
+ android:scaleX="-1">
<!-- Tint color to be set directly -->
<path android:fillColor="#FFFFFFFF"
android:pathData="M12.02,10.83L9.25,8.06l2.77,-2.77l1.12,1.12l-0.85,0.86h5.16c0.72,0 1.31,0.56 1.31,1.26v9.16l-1.58,-1.58V8.85h-4.89l0.86,0.86L12.02,10.83zM15.98,17.17l-1.12,1.12l0.85,0.86h-4.88v-7.26L9.25,10.3v9.17c0,0.7 0.59,1.26 1.31,1.26h5.16v0.01l-0.85,0.85l1.12,1.12l2.77,-2.77L15.98,17.17z"/>
@@ -107,8 +107,8 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="100"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="0"
+ android:valueTo="90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -118,14 +118,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonStartAngle"/>
+ android:valueFrom="0"
+ android:valueTo="0"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="0"
+ android:valueTo="90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -135,14 +135,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonStartAngle"/>
+ android:valueFrom="0"
+ android:valueTo="0"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="0"
+ android:valueTo="90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -152,14 +152,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonStartAngle"/>
+ android:valueFrom="0"
+ android:valueTo="0"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="0"
+ android:valueTo="90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -169,14 +169,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonStartAngle"/>
+ android:valueFrom="0"
+ android:valueTo="0"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="0"
+ android:valueTo="90">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button_cw_start_90.xml
similarity index 84%
copy from packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml
copy to packages/SystemUI/res/drawable/ic_sysbar_rotate_button_cw_start_90.xml
index 3304c19..0dc67b0 100644
--- a/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml
+++ b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button_cw_start_90.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2017 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.
@@ -23,7 +23,7 @@
android:viewportHeight="28.0">
<!-- Use scaleX to flip icon so arrows always point in the direction of motion -->
<group android:name="icon" android:pivotX="14" android:pivotY="14"
- android:scaleX="?attr/rotateButtonScaleX">
+ android:scaleX="-1">
<!-- Tint color to be set directly -->
<path android:fillColor="#FFFFFFFF"
android:pathData="M12.02,10.83L9.25,8.06l2.77,-2.77l1.12,1.12l-0.85,0.86h5.16c0.72,0 1.31,0.56 1.31,1.26v9.16l-1.58,-1.58V8.85h-4.89l0.86,0.86L12.02,10.83zM15.98,17.17l-1.12,1.12l0.85,0.86h-4.88v-7.26L9.25,10.3v9.17c0,0.7 0.59,1.26 1.31,1.26h5.16v0.01l-0.85,0.85l1.12,1.12l2.77,-2.77L15.98,17.17z"/>
@@ -107,8 +107,8 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="100"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="90"
+ android:valueTo="180">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -118,14 +118,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonStartAngle"/>
+ android:valueFrom="90"
+ android:valueTo="90"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="90"
+ android:valueTo="180">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -135,14 +135,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonStartAngle"/>
+ android:valueFrom="90"
+ android:valueTo="90"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="90"
+ android:valueTo="180">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -152,14 +152,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonStartAngle"/>
+ android:valueFrom="90"
+ android:valueTo="90"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="90"
+ android:valueTo="180">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
@@ -169,14 +169,14 @@
<objectAnimator android:propertyName="rotation"
android:startOffset="1300"
android:duration="100"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonStartAngle"/>
+ android:valueFrom="90"
+ android:valueTo="90"/>
<!-- Icon rotation with start timing offset after fade in -->
<objectAnimator android:propertyName="rotation"
android:duration="600"
- android:valueFrom="?attr/rotateButtonStartAngle"
- android:valueTo="?attr/rotateButtonEndAngle">
+ android:valueFrom="90"
+ android:valueTo="180">
<aapt:attr name="android:interpolator">
<pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
</aapt:attr>
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-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 8e10230..27bc3ab 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -1021,9 +1021,9 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Параметрлер"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Ұлғайту терезесі"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Ұлғайту терезесінің басқару элементтері"</string>
- <string name="quick_controls_title" msgid="6839108006171302273">"Құрылғы басқару виджеттері"</string>
+ <string name="quick_controls_title" msgid="6839108006171302273">"Құрылғыны басқару элементтері"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Жалғанған құрылғылар үшін басқару виджеттерін қосу"</string>
- <string name="quick_controls_setup_title" msgid="8901436655997849822">"Құрылғы басқару виджеттерін реттеу"</string>
+ <string name="quick_controls_setup_title" msgid="8901436655997849822">"Құрылғыны басқару элементтерін реттеу"</string>
<string name="quick_controls_setup_subtitle" msgid="1681506617879773824">"Басқару элементтерін шығару үшін қуат түймесін басып тұрыңыз."</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Басқару элементтері енгізілетін қолданбаны таңдаңыз"</string>
<plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
@@ -1046,7 +1046,7 @@
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Басқару элементтері жүктелмеді. Қолданба параметрлерінің өзгермегенін тексеру үшін <xliff:g id="APP">%s</xliff:g> қолданбасын қараңыз."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Үйлесімді басқару элементтері қолжетімді емес."</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Басқа"</string>
- <string name="controls_dialog_title" msgid="2343565267424406202">"Құрылғы басқару виджеттеріне қосу"</string>
+ <string name="controls_dialog_title" msgid="2343565267424406202">"Құрылғы басқару элементтеріне қосу"</string>
<string name="controls_dialog_ok" msgid="2770230012857881822">"Енгізу"</string>
<string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> ұсынған"</string>
<string name="controls_dialog_confirmation" msgid="586517302736263447">"Басқару элементтері жаңартылды"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 6b8b674..341efff 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -811,7 +811,7 @@
<string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Notificações"</string>
<string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Atalhos de teclado"</string>
<string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Alterar esquema de teclado"</string>
- <string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"Aplicações"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"Apps"</string>
<string name="keyboard_shortcut_group_applications_assist" msgid="771606231466098742">"Assistência"</string>
<string name="keyboard_shortcut_group_applications_browser" msgid="2776211137869809251">"Navegador"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2807268086386201060">"Contactos"</string>
@@ -965,7 +965,7 @@
<string name="qs_dnd_until" msgid="7844269319043747955">"Até à(s) <xliff:g id="ID_1">%s</xliff:g>"</string>
<string name="qs_dnd_keep" msgid="3829697305432866434">"Manter"</string>
<string name="qs_dnd_replace" msgid="7712119051407052689">"Substituir"</string>
- <string name="running_foreground_services_title" msgid="5137313173431186685">"Aplicações em execução em segundo plano"</string>
+ <string name="running_foreground_services_title" msgid="5137313173431186685">"Apps em execução em segundo plano"</string>
<string name="running_foreground_services_msg" msgid="3009459259222695385">"Toque para obter detalhes acerca da utilização da bateria e dos dados"</string>
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Pretende desativar os dados móveis?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Não terá acesso a dados ou à Internet através do operador <xliff:g id="CARRIER">%s</xliff:g>. A Internet estará disponível apenas por Wi-Fi."</string>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 10c7210..84dbd60 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -133,11 +133,6 @@
<attr name="buttonStrokeWidth" format="dimension" />
</declare-styleable>
- <!-- Used to style rotate suggestion button AVD animations -->
- <attr name="rotateButtonStartAngle" format="float" />
- <attr name="rotateButtonEndAngle" format="float" />
- <attr name="rotateButtonScaleX" format="float" />
-
<!-- Used to style charging animation AVD animation -->
<attr name="chargingAnimColor" format="color" />
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/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 68c2a38..9e5b94e 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -604,31 +604,6 @@
<item name="android:colorBackground">?android:attr/colorSecondary</item>
</style>
- <!-- Used to style rotate suggestion button AVD animations -->
- <style name="RotateButtonCCWStart0">
- <item name="rotateButtonStartAngle">0</item>
- <item name="rotateButtonEndAngle">-90</item>
- <item name="rotateButtonScaleX">1</item>
- </style>
-
- <style name="RotateButtonCCWStart90">
- <item name="rotateButtonStartAngle">90</item>
- <item name="rotateButtonEndAngle">0</item>
- <item name="rotateButtonScaleX">1</item>
- </style>
-
- <style name="RotateButtonCWStart0">
- <item name="rotateButtonStartAngle">0</item>
- <item name="rotateButtonEndAngle">90</item>
- <item name="rotateButtonScaleX">-1</item>
- </style>
-
- <style name="RotateButtonCWStart90">
- <item name="rotateButtonStartAngle">90</item>
- <item name="rotateButtonEndAngle">180</item>
- <item name="rotateButtonScaleX">-1</item>
- </style>
-
<style name="MediaPlayer.Button" parent="@android:style/Widget.Material.Button.Borderless.Small">
<item name="android:background">@drawable/qs_media_light_source</item>
<item name="android:tint">@android:color/white</item>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 65bf7e6..97317cf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -138,7 +138,7 @@
case PROMPT_REASON_USER_REQUEST:
return R.string.kg_prompt_reason_user_request;
case PROMPT_REASON_PREPARE_FOR_UPDATE:
- return R.string.kg_prompt_reason_prepare_for_update_password;
+ return R.string.kg_prompt_reason_timeout_password;
case PROMPT_REASON_NONE:
return 0;
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index ad92f8f..c4a9fcb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -440,8 +440,7 @@
mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_user_request);
break;
case PROMPT_REASON_PREPARE_FOR_UPDATE:
- mSecurityMessageDisplay.setMessage(
- R.string.kg_prompt_reason_prepare_for_update_pattern);
+ mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern);
break;
case PROMPT_REASON_NONE:
break;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 6d865ab..c7f27cf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -117,7 +117,7 @@
case PROMPT_REASON_USER_REQUEST:
return R.string.kg_prompt_reason_user_request;
case PROMPT_REASON_PREPARE_FOR_UPDATE:
- return R.string.kg_prompt_reason_prepare_for_update_pin;
+ return R.string.kg_prompt_reason_timeout_pin;
case PROMPT_REASON_NONE:
return 0;
default:
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 02d2b8e..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;
@@ -122,17 +123,17 @@
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.leak.LeakReporter;
import com.android.systemui.util.sensors.AsyncSensorManager;
-import com.android.systemui.wm.DisplayController;
-import com.android.systemui.wm.DisplayImeController;
-import com.android.systemui.wm.SystemWindows;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.SystemWindows;
import java.util.function.Consumer;
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/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index ab7af43..f35322b 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -22,7 +22,6 @@
import android.hardware.SensorManager;
import android.net.Uri;
import android.provider.DeviceConfig;
-import android.util.DisplayMetrics;
import android.view.MotionEvent;
import androidx.annotation.NonNull;
@@ -62,7 +61,7 @@
private static final String PROXIMITY_SENSOR_TAG = "FalsingManager";
private final ProximitySensor mProximitySensor;
- private final DisplayMetrics mDisplayMetrics;
+ private final FalsingDataProvider mFalsingDataProvider;
private FalsingManager mInternalFalsingManager;
private DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener;
private final DeviceConfigProxy mDeviceConfig;
@@ -74,18 +73,19 @@
@Inject
FalsingManagerProxy(Context context, PluginManager pluginManager, @Main Executor executor,
- DisplayMetrics displayMetrics, ProximitySensor proximitySensor,
+ ProximitySensor proximitySensor,
DeviceConfigProxy deviceConfig, DockManager dockManager,
KeyguardUpdateMonitor keyguardUpdateMonitor,
DumpManager dumpManager,
@UiBackground Executor uiBgExecutor,
- StatusBarStateController statusBarStateController) {
- mDisplayMetrics = displayMetrics;
+ StatusBarStateController statusBarStateController,
+ FalsingDataProvider falsingDataProvider) {
mProximitySensor = proximitySensor;
mDockManager = dockManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mUiBgExecutor = uiBgExecutor;
mStatusBarStateController = statusBarStateController;
+ mFalsingDataProvider = falsingDataProvider;
mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
mProximitySensor.setDelay(SensorManager.SENSOR_DELAY_GAME);
mDeviceConfig = deviceConfig;
@@ -143,7 +143,7 @@
mInternalFalsingManager = new FalsingManagerImpl(context, mUiBgExecutor);
} else {
mInternalFalsingManager = new BrightLineFalsingManager(
- new FalsingDataProvider(mDisplayMetrics),
+ mFalsingDataProvider,
mKeyguardUpdateMonitor,
mProximitySensor,
mDeviceConfig,
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
index 95d5ad9..a50f9ce 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -132,7 +132,9 @@
}
private void registerSensors() {
- mProximitySensor.register(mSensorEventListener);
+ if (!mDataProvider.isWirelessCharging()) {
+ mProximitySensor.register(mSensorEventListener);
+ }
}
private void unregisterSensors() {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
index 5494c64..ea46441 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
@@ -22,10 +22,13 @@
import android.view.MotionEvent.PointerProperties;
import com.android.systemui.classifier.Classifier;
+import com.android.systemui.statusbar.policy.BatteryController;
import java.util.ArrayList;
import java.util.List;
+import javax.inject.Inject;
+
/**
* Acts as a cache and utility class for FalsingClassifiers.
*/
@@ -36,6 +39,7 @@
private final int mWidthPixels;
private final int mHeightPixels;
+ private final BatteryController mBatteryController;
private final float mXdpi;
private final float mYdpi;
@@ -50,11 +54,13 @@
private MotionEvent mFirstRecentMotionEvent;
private MotionEvent mLastMotionEvent;
- public FalsingDataProvider(DisplayMetrics displayMetrics) {
+ @Inject
+ public FalsingDataProvider(DisplayMetrics displayMetrics, BatteryController batteryController) {
mXdpi = displayMetrics.xdpi;
mYdpi = displayMetrics.ydpi;
mWidthPixels = displayMetrics.widthPixels;
mHeightPixels = displayMetrics.heightPixels;
+ mBatteryController = batteryController;
FalsingClassifier.logInfo("xdpi, ydpi: " + getXdpi() + ", " + getYdpi());
FalsingClassifier.logInfo("width, height: " + getWidthPixels() + ", " + getHeightPixels());
@@ -177,6 +183,11 @@
return mLastMotionEvent.getY() < mFirstRecentMotionEvent.getY();
}
+ /** Returns true if phone is being charged without a cable. */
+ boolean isWirelessCharging() {
+ return mBatteryController.isWirelessCharging();
+ }
+
private void recalculateData() {
if (!mDirty) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
index 58807f0..aa3e193 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
@@ -49,6 +49,11 @@
private const val SUGGESTED_STRUCTURES = 6L
private const val SUGGESTED_CONTROLS_REQUEST =
ControlsControllerImpl.SUGGESTED_CONTROLS_PER_STRUCTURE * SUGGESTED_STRUCTURES
+
+ private val emptyCallback = object : ControlsBindingController.LoadCallback {
+ override fun accept(controls: List<Control>) {}
+ override fun error(message: String) {}
+ }
}
private var currentUser = UserHandle.of(ActivityManager.getCurrentUser())
@@ -283,7 +288,7 @@
}
private inner class LoadSubscriber(
- val callback: ControlsBindingController.LoadCallback,
+ var callback: ControlsBindingController.LoadCallback,
val requestLimit: Long
) : IControlsSubscriber.Stub() {
val loadedControls = ArrayList<Control>()
@@ -337,6 +342,10 @@
if (isTerminated.get()) return
_loadCancelInternal = {}
+
+ // Reassign the callback to clear references to other areas of code. Binders such as
+ // this may not be GC'd right away, so do not hold onto these references.
+ callback = emptyCallback
currentProvider?.cancelLoadTimeout()
backgroundExecutor.execute {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index ec8bfc6..977e46a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -76,7 +76,8 @@
private const val LOAD_TIMEOUT_SECONDS = 20L // seconds
private const val MAX_BIND_RETRIES = 5
private const val DEBUG = true
- private val BIND_FLAGS = Context.BIND_AUTO_CREATE or Context.BIND_FOREGROUND_SERVICE
+ private val BIND_FLAGS = Context.BIND_AUTO_CREATE or Context.BIND_FOREGROUND_SERVICE or
+ Context.BIND_NOT_PERCEPTIBLE
}
private val intent = Intent().apply {
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/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index a30d934..3d31070 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -22,7 +22,6 @@
import android.content.Context;
import android.content.SharedPreferences;
import android.hardware.display.AmbientDisplayConfiguration;
-import android.hardware.display.NightDisplayListener;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -78,7 +77,7 @@
*
* See SystemUI/docs/dagger.md
*/
-@Module
+@Module(includes = {NightDisplayListenerModule.class})
public class DependencyProvider {
@Singleton
@@ -153,13 +152,6 @@
@Singleton
@Provides
- public NightDisplayListener provideNightDisplayListener(Context context,
- @Background Handler bgHandler) {
- return new NightDisplayListener(context, bgHandler);
- }
-
- @Singleton
- @Provides
public PluginManager providePluginManager(Context context) {
return new PluginManagerImpl(context, new PluginInitializerImpl());
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/NightDisplayListenerModule.java b/packages/SystemUI/src/com/android/systemui/dagger/NightDisplayListenerModule.java
new file mode 100644
index 0000000..7091105
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/NightDisplayListenerModule.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.dagger;
+
+import android.content.Context;
+import android.hardware.display.NightDisplayListener;
+import android.os.Handler;
+import android.os.UserHandle;
+
+import com.android.systemui.dagger.qualifiers.Background;
+
+import javax.inject.Inject;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Module for providing a {@link NightDisplayListener}.
+ */
+@Module
+public class NightDisplayListenerModule {
+
+ /**
+ * Provides a {@link NightDisplayListener}.
+ *
+ * The provided listener is associated with the user as returned by
+ * {@link android.app.ActivityManager#getCurrentUser}, making an IPC call on its creation.
+ * If the current user is known, prefer using a {@link Builder}.
+ */
+ @Provides
+ public NightDisplayListener provideNightDisplayListener(Context context,
+ @Background Handler bgHandler) {
+ return new NightDisplayListener(context, bgHandler);
+ }
+
+ /**
+ * Builder to create instances of {@link NightDisplayListener}.
+ *
+ * It uses {@link UserHandle#USER_SYSTEM} as the default user.
+ */
+ public static class Builder {
+ private final Context mContext;
+ private final Handler mBgHandler;
+ private int mUserId = UserHandle.USER_SYSTEM;
+
+ @Inject
+ public Builder(Context context, @Background Handler bgHandler) {
+ mContext = context;
+ mBgHandler = bgHandler;
+ }
+
+ /**
+ * Set the userId for this builder
+ */
+ public Builder setUser(int userId) {
+ mUserId = userId;
+ return this;
+ }
+
+ /**
+ * Build a {@link NightDisplayListener} for the set user.
+ */
+ public NightDisplayListener build() {
+ return new NightDisplayListener(mContext, mUserId, mBgHandler);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
index f683a63..251ce13 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
@@ -39,6 +39,7 @@
import android.content.res.Resources;
import android.hardware.SensorManager;
import android.hardware.SensorPrivacyManager;
+import android.hardware.display.ColorDisplayManager;
import android.hardware.display.DisplayManager;
import android.media.AudioManager;
import android.media.MediaRouter2Manager;
@@ -106,6 +107,12 @@
@Provides
@Singleton
+ static ColorDisplayManager provideColorDisplayManager(Context context) {
+ return context.getSystemService(ColorDisplayManager.class);
+ }
+
+ @Provides
+ @Singleton
static ConnectivityManager provideConnectivityManagager(Context context) {
return context.getSystemService(ConnectivityManager.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index cd0ba29..803e56d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -59,6 +59,7 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.wmshell.WindowManagerShellModule;
import javax.inject.Named;
import javax.inject.Singleton;
@@ -71,7 +72,7 @@
* A dagger module for injecting default implementations of components of System UI that may be
* overridden by the System UI implementation.
*/
-@Module(includes = {DividerModule.class, QSModule.class})
+@Module(includes = {DividerModule.class, QSModule.class, WindowManagerShellModule.class})
public abstract class SystemUIDefaultModule {
@Singleton
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 0800a20..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;
@@ -408,10 +411,13 @@
break;
case DOZE_PULSING:
case DOZE_PULSING_BRIGHT:
- case DOZE_AOD_DOCKED:
mWantProx = true;
mWantTouchScreenSensors = false;
break;
+ case DOZE_AOD_DOCKED:
+ mWantProx = false;
+ mWantTouchScreenSensors = false;
+ break;
case DOZE_PULSE_DONE:
mDozeSensors.requestTemporaryDisable();
// A pulse will temporarily disable sensors that require a touch screen.
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index e12b7dd..075318b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -306,7 +306,7 @@
private fun updatePageIndicator() {
val numPages = mediaContent.getChildCount()
- pageIndicator.setNumPages(numPages, Color.WHITE)
+ pageIndicator.setNumPages(numPages)
if (numPages == 1) {
pageIndicator.setLocation(0f)
}
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 299ae5b..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
@@ -316,18 +320,13 @@
as MediaSession.Token?
val metadata = mediaControllerFactory.create(token).metadata
- if (metadata == null) {
- // TODO: handle this better, removing media notification
- return
- }
-
// Foreground and Background colors computed from album art
val notif: Notification = sbn.notification
- var artworkBitmap = metadata.getBitmap(MediaMetadata.METADATA_KEY_ART)
+ var artworkBitmap = metadata?.getBitmap(MediaMetadata.METADATA_KEY_ART)
if (artworkBitmap == null) {
- artworkBitmap = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)
+ artworkBitmap = metadata?.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)
}
- if (artworkBitmap == null) {
+ if (artworkBitmap == null && metadata != null) {
artworkBitmap = loadBitmapFromUri(metadata)
}
val artWorkIcon = if (artworkBitmap == null) {
@@ -363,16 +362,16 @@
val smallIconDrawable: Drawable = sbn.notification.smallIcon.loadDrawable(context)
// Song name
- var song: CharSequence? = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
+ var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
if (song == null) {
- song = metadata.getString(MediaMetadata.METADATA_KEY_TITLE)
+ song = metadata?.getString(MediaMetadata.METADATA_KEY_TITLE)
}
if (song == null) {
song = HybridGroupManager.resolveTitle(notif)
}
// Artist name
- var artist: CharSequence? = metadata.getString(MediaMetadata.METADATA_KEY_ARTIST)
+ var artist: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_ARTIST)
if (artist == null) {
artist = HybridGroupManager.resolveText(notif)
}
@@ -388,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
}
@@ -432,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
}
}
@@ -519,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/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index 143f849..ae7f66b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -19,8 +19,12 @@
import android.content.Context
import android.media.MediaRouter2Manager
import android.media.session.MediaController
+import androidx.annotation.AnyThread
+import androidx.annotation.MainThread
+import androidx.annotation.WorkerThread
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.Dumpable
import com.android.systemui.dump.DumpManager
@@ -39,11 +43,12 @@
private val localMediaManagerFactory: LocalMediaManagerFactory,
private val mr2manager: MediaRouter2Manager,
@Main private val fgExecutor: Executor,
+ @Background private val bgExecutor: Executor,
private val mediaDataManager: MediaDataManager,
private val dumpManager: DumpManager
) : MediaDataManager.Listener, Dumpable {
private val listeners: MutableSet<Listener> = mutableSetOf()
- private val entries: MutableMap<String, Token> = mutableMapOf()
+ private val entries: MutableMap<String, Entry> = mutableMapOf()
init {
mediaDataManager.addListener(this)
@@ -71,7 +76,7 @@
val controller = data.token?.let {
MediaController(context, it)
}
- entry = Token(key, oldKey, controller,
+ entry = Entry(key, oldKey, controller,
localMediaManagerFactory.create(data.packageName))
entries[key] = entry
entry.start()
@@ -99,6 +104,7 @@
}
}
+ @MainThread
private fun processDevice(key: String, oldKey: String?, device: MediaDevice?) {
val enabled = device != null
val data = MediaDeviceData(enabled, device?.iconWithoutBackground, device?.name)
@@ -114,12 +120,13 @@
fun onKeyRemoved(key: String)
}
- private inner class Token(
+ private inner class Entry(
val key: String,
val oldKey: String?,
val controller: MediaController?,
val localMediaManager: LocalMediaManager
) : LocalMediaManager.DeviceCallback {
+
val token
get() = controller?.sessionToken
private var started = false
@@ -127,20 +134,27 @@
set(value) {
if (!started || value != field) {
field = value
- processDevice(key, oldKey, value)
+ fgExecutor.execute {
+ processDevice(key, oldKey, value)
+ }
}
}
- fun start() {
+
+ @AnyThread
+ fun start() = bgExecutor.execute {
localMediaManager.registerCallback(this)
localMediaManager.startScan()
updateCurrent()
started = true
}
- fun stop() {
+
+ @AnyThread
+ fun stop() = bgExecutor.execute {
started = false
localMediaManager.stopScan()
localMediaManager.unregisterCallback(this)
}
+
fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
val route = controller?.let {
mr2manager.getRoutingSessionForMediaController(it)
@@ -152,14 +166,18 @@
println(" route=$route")
}
}
- override fun onDeviceListUpdate(devices: List<MediaDevice>?) = fgExecutor.execute {
+
+ override fun onDeviceListUpdate(devices: List<MediaDevice>?) = bgExecutor.execute {
updateCurrent()
}
+
override fun onSelectedDeviceStateChanged(device: MediaDevice, state: Int) {
- fgExecutor.execute {
+ bgExecutor.execute {
updateCurrent()
}
}
+
+ @WorkerThread
private fun updateCurrent() {
val device = localMediaManager.getCurrentConnectedDevice()
controller?.let {
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
index 1dca3f1..9e326aa 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
@@ -71,7 +71,7 @@
/** ViewModel for seek bar in QS media player. */
class SeekBarViewModel @Inject constructor(@Background private val bgExecutor: RepeatableExecutor) {
- private var _data = Progress(false, false, null, null)
+ private var _data = Progress(false, false, null, 0)
set(value) {
field = value
_progress.postValue(value)
@@ -186,10 +186,10 @@
val mediaMetadata = controller?.metadata
val seekAvailable = ((playbackState?.actions ?: 0L) and PlaybackState.ACTION_SEEK_TO) != 0L
val position = playbackState?.position?.toInt()
- val duration = mediaMetadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt()
+ val duration = mediaMetadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt() ?: 0
val enabled = if (playbackState == null ||
playbackState?.getState() == PlaybackState.STATE_NONE ||
- (duration != null && duration <= 0)) false else true
+ (duration <= 0)) false else true
_data = Progress(enabled, seekAvailable, position, duration)
checkIfPollingNeeded()
}
@@ -408,6 +408,6 @@
val enabled: Boolean,
val seekAvailable: Boolean,
val elapsedTime: Int?,
- val duration: Int?
+ val duration: Int
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
index 16e05f1..c0b9258 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -27,7 +27,6 @@
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
-import android.view.Surface;
import android.view.SurfaceControl;
import android.window.DisplayAreaInfo;
import android.window.DisplayAreaOrganizer;
@@ -39,7 +38,7 @@
import com.android.internal.os.SomeArgs;
import com.android.systemui.Dumpable;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java
index f995bf9..71c5f80 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java
@@ -41,8 +41,8 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.NavigationModeController;
-import com.android.systemui.wm.DisplayChangeController;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayChangeController;
+import com.android.wm.shell.common.DisplayController;
import javax.inject.Inject;
import javax.inject.Singleton;
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
index 586761b..70a81aa 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
@@ -33,8 +33,8 @@
import com.android.systemui.model.SysUiState;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.wm.DisplayChangeController;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayChangeController;
+import com.android.wm.shell.common.DisplayController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
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/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 665b90e..df3aead 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -39,8 +39,8 @@
import android.view.Gravity;
import android.window.WindowContainerTransaction;
-import com.android.systemui.wm.DisplayController;
-import com.android.systemui.wm.DisplayLayout;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 35e56ee..312d6d6 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -58,8 +58,8 @@
import com.android.internal.os.SomeArgs;
import com.android.systemui.pip.phone.PipUpdateThread;
import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.wm.DisplayController;
import com.android.wm.shell.R;
+import com.android.wm.shell.common.DisplayController;
import java.io.PrintWriter;
import java.util.ArrayList;
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 02bf475..582cd04 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -56,9 +56,8 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.FloatingContentCoordinator;
-import com.android.systemui.wm.DisplayChangeController;
-import com.android.systemui.wm.DisplayController;
-import com.android.systemui.wm.DisplayLayout;
+import com.android.wm.shell.common.DisplayChangeController;
+import com.android.wm.shell.common.DisplayController;
import java.io.PrintWriter;
@@ -79,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/pip/phone/PipMediaController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
index 05b9b0e..361aafa 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
@@ -36,9 +36,9 @@
import android.os.UserHandle;
import com.android.systemui.Dependency;
+import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.wm.shell.R;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index bd9ddc57..5434b62 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -56,6 +56,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.logging.MetricsLoggerWrapper;
+import com.android.systemui.R;
import com.android.systemui.model.SysUiState;
import com.android.systemui.pip.PipAnimationController;
import com.android.systemui.pip.PipBoundsHandler;
@@ -67,7 +68,6 @@
import com.android.systemui.util.FloatingContentCoordinator;
import com.android.systemui.util.animation.PhysicsAnimator;
import com.android.systemui.util.magnetictarget.MagnetizedObject;
-import com.android.wm.shell.R;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 3a3d629..2138f09 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -46,6 +46,7 @@
import android.view.DisplayInfo;
import com.android.systemui.Dependency;
+import com.android.systemui.R;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.pip.BasePipManager;
@@ -57,7 +58,6 @@
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.stackdivider.Divider;
-import com.android.wm.shell.R;
import java.util.ArrayList;
import java.util.List;
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/PageIndicator.java b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
index 2c76d70..f8655cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
@@ -10,10 +10,18 @@
import android.view.ViewGroup;
import android.widget.ImageView;
+import androidx.annotation.NonNull;
+
+import com.android.settingslib.Utils;
import com.android.systemui.R;
import java.util.ArrayList;
+/**
+ * Page indicator for using with pageable layouts
+ *
+ * Supports {@code android.R.attr.tint}. If missing, it will use the current accent color.
+ */
public class PageIndicator extends ViewGroup {
private static final String TAG = "PageIndicator";
@@ -31,12 +39,22 @@
private final int mPageIndicatorWidth;
private final int mPageIndicatorHeight;
private final int mPageDotWidth;
+ private @NonNull ColorStateList mTint;
private int mPosition = -1;
private boolean mAnimating;
public PageIndicator(Context context, AttributeSet attrs) {
super(context, attrs);
+
+ TypedArray array = context.obtainStyledAttributes(attrs, new int[]{android.R.attr.tint});
+ if (array.hasValue(0)) {
+ mTint = array.getColorStateList(0);
+ } else {
+ mTint = Utils.getColorAccent(context);
+ }
+ array.recycle();
+
mPageIndicatorWidth =
(int) mContext.getResources().getDimension(R.dimen.qs_page_indicator_width);
mPageIndicatorHeight =
@@ -45,15 +63,6 @@
}
public void setNumPages(int numPages) {
- TypedArray array = getContext().obtainStyledAttributes(
- new int[]{android.R.attr.colorControlActivated});
- int color = array.getColor(0, 0);
- array.recycle();
- setNumPages(numPages, color);
- }
-
- /** Overload of setNumPages that allows the indicator color to be specified.*/
- public void setNumPages(int numPages, int color) {
setVisibility(numPages > 1 ? View.VISIBLE : View.GONE);
if (numPages == getChildCount()) {
return;
@@ -67,13 +76,41 @@
while (numPages > getChildCount()) {
ImageView v = new ImageView(mContext);
v.setImageResource(R.drawable.minor_a_b);
- v.setImageTintList(ColorStateList.valueOf(color));
+ v.setImageTintList(mTint);
addView(v, new LayoutParams(mPageIndicatorWidth, mPageIndicatorHeight));
}
// Refresh state.
setIndex(mPosition >> 1);
}
+ /**
+ * @return the current tint list for this view.
+ */
+ @NonNull
+ public ColorStateList getTintList() {
+ return mTint;
+ }
+
+ /**
+ * Set the color for this view.
+ * <br>
+ * Calling this will change the color of the current view and any new dots that are added to it.
+ * @param color the new color
+ */
+ public void setTintList(@NonNull ColorStateList color) {
+ if (color.equals(mTint)) {
+ return;
+ }
+ mTint = color;
+ final int N = getChildCount();
+ for (int i = 0; i < N; i++) {
+ View v = getChildAt(i);
+ if (v instanceof ImageView) {
+ ((ImageView) v).setImageTintList(mTint);
+ }
+ }
+ }
+
public void setLocation(float location) {
int index = (int) location;
setContentDescription(getContext().getString(R.string.accessibility_quick_settings_page,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 3eed8ad..560998b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -508,7 +508,6 @@
mPageListener.onPageChanged(isLayoutRtl() ? position == mPages.size() - 1
: position == 0);
}
-
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index 1e8c4d8..290ab85 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -20,7 +20,6 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.external.TileServices;
-import com.android.systemui.qs.logging.QSLogger;
import java.util.Collection;
@@ -31,7 +30,6 @@
void openPanels();
Context getContext();
Context getUserContext();
- QSLogger getQSLogger();
UiEventLogger getUiEventLogger();
Collection<QSTile> getTiles();
void addCallback(Callback callback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 1f3967c..3b16a4e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -179,10 +179,6 @@
onTuningChanged(TILES_SETTING, value);
}
- public QSLogger getQSLogger() {
- return mQSLogger;
- }
-
@Override
public UiEventLogger getUiEventLogger() {
return mUiEventLogger;
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/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 30e0a76..19c7b6c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -29,7 +29,9 @@
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.provider.Settings;
import android.service.quicksettings.IQSTileService;
@@ -42,16 +44,27 @@
import android.view.WindowManagerGlobal;
import android.widget.Switch;
+import androidx.annotation.NonNull;
+
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.State;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import java.util.Objects;
+import javax.inject.Inject;
+
+import dagger.Lazy;
+
public class CustomTile extends QSTileImpl<State> implements TileChangeListener {
public static final String PREFIX = "custom(";
@@ -79,8 +92,19 @@
private boolean mIsTokenGranted;
private boolean mIsShowingDialog;
- private CustomTile(QSHost host, String action, Context userContext) {
- super(host);
+ private CustomTile(
+ QSHost host,
+ Looper backgroundLooper,
+ Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ String action,
+ Context userContext
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mWindowManager = WindowManagerGlobal.getWindowManagerService();
mComponent = ComponentName.unflattenFromString(action);
mTile = new Tile();
@@ -397,7 +421,7 @@
return ComponentName.unflattenFromString(action);
}
- public static CustomTile create(QSHost host, String spec, Context userContext) {
+ private static String getAction(String spec) {
if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
throw new IllegalArgumentException("Bad custom tile spec: " + spec);
}
@@ -405,6 +429,81 @@
if (action.isEmpty()) {
throw new IllegalArgumentException("Empty custom tile spec action");
}
- return new CustomTile(host, action, userContext);
+ return action;
+ }
+
+ /**
+ * Create a {@link CustomTile} for a given spec and user.
+ *
+ * @param builder including injected common dependencies.
+ * @param spec as provided by {@link CustomTile#toSpec}
+ * @param userContext context for the user that is creating this tile.
+ * @return a new {@link CustomTile}
+ */
+ public static CustomTile create(Builder builder, String spec, Context userContext) {
+ return builder
+ .setSpec(spec)
+ .setUserContext(userContext)
+ .build();
+ }
+
+ public static class Builder {
+ final Lazy<QSHost> mQSHostLazy;
+ final Looper mBackgroundLooper;
+ final Handler mMainHandler;
+ final MetricsLogger mMetricsLogger;
+ final StatusBarStateController mStatusBarStateController;
+ final ActivityStarter mActivityStarter;
+ final QSLogger mQSLogger;
+
+ Context mUserContext;
+ String mSpec = "";
+
+ @Inject
+ public Builder(
+ Lazy<QSHost> hostLazy,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger
+ ) {
+ mQSHostLazy = hostLazy;
+ mBackgroundLooper = backgroundLooper;
+ mMainHandler = mainHandler;
+ mMetricsLogger = metricsLogger;
+ mStatusBarStateController = statusBarStateController;
+ mActivityStarter = activityStarter;
+ mQSLogger = qsLogger;
+ }
+
+ Builder setSpec(@NonNull String spec) {
+ mSpec = spec;
+ return this;
+ }
+
+ Builder setUserContext(@NonNull Context userContext) {
+ mUserContext = userContext;
+ return this;
+ }
+
+ CustomTile build() {
+ if (mUserContext == null) {
+ throw new NullPointerException("UserContext cannot be null");
+ }
+ String action = getAction(mSpec);
+ return new CustomTile(
+ mQSHostLazy.get(),
+ mBackgroundLooper,
+ mMainHandler,
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQSLogger,
+ action,
+ mUserContext
+ );
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index c182a58..69a6fe1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -80,9 +80,12 @@
private final Provider<ScreenRecordTile> mScreenRecordTileProvider;
private final Lazy<QSHost> mQsHostLazy;
+ private final Provider<CustomTile.Builder> mCustomTileBuilderProvider;
@Inject
- public QSFactoryImpl(Lazy<QSHost> qsHostLazy,
+ public QSFactoryImpl(
+ Lazy<QSHost> qsHostLazy,
+ Provider<CustomTile.Builder> customTileBuilderProvider,
Provider<WifiTile> wifiTileProvider,
Provider<BluetoothTile> bluetoothTileProvider,
Provider<CellularTile> cellularTileProvider,
@@ -104,6 +107,8 @@
Provider<UiModeNightTile> uiModeNightTileProvider,
Provider<ScreenRecordTile> screenRecordTileProvider) {
mQsHostLazy = qsHostLazy;
+ mCustomTileBuilderProvider = customTileBuilderProvider;
+
mWifiTileProvider = wifiTileProvider;
mBluetoothTileProvider = bluetoothTileProvider;
mCellularTileProvider = cellularTileProvider;
@@ -179,8 +184,8 @@
// Custom tiles
if (tileSpec.startsWith(CustomTile.PREFIX)) {
- return CustomTile.create(mQsHostLazy.get(), tileSpec,
- mQsHostLazy.get().getUserContext());
+ return CustomTile.create(
+ mCustomTileBuilderProvider.get(), tileSpec, mQsHostLazy.get().getUserContext());
}
// Debug tiles.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index d110792..8c485a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -55,7 +55,6 @@
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.Utils;
-import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.Prefs;
import com.android.systemui.plugins.ActivityStarter;
@@ -93,12 +92,12 @@
protected final QSHost mHost;
protected final Context mContext;
// @NonFinalForTesting
- protected H mHandler = new H(Dependency.get(Dependency.BG_LOOPER));
- protected final Handler mUiHandler = new Handler(Looper.getMainLooper());
+ protected final H mHandler;
+ protected final Handler mUiHandler;
private final ArraySet<Object> mListeners = new ArraySet<>();
- private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
- private final StatusBarStateController
- mStatusBarStateController = Dependency.get(StatusBarStateController.class);
+ private final MetricsLogger mMetricsLogger;
+ private final StatusBarStateController mStatusBarStateController;
+ protected final ActivityStarter mActivityStarter;
private final UiEventLogger mUiEventLogger;
private final QSLogger mQSLogger;
@@ -151,14 +150,29 @@
*/
abstract public int getMetricsCategory();
- protected QSTileImpl(QSHost host) {
+ protected QSTileImpl(
+ QSHost host,
+ Looper backgroundLooper,
+ Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger
+ ) {
mHost = host;
mContext = host.getContext();
mInstanceId = host.getNewInstanceId();
+ mUiEventLogger = host.getUiEventLogger();
+
+ mUiHandler = mainHandler;
+ mHandler = new H(backgroundLooper);
+ mQSLogger = qsLogger;
+ mMetricsLogger = metricsLogger;
+ mStatusBarStateController = statusBarStateController;
+ mActivityStarter = activityStarter;
+
mState = newTileState();
mTmpState = newTileState();
- mQSLogger = host.getQSLogger();
- mUiEventLogger = host.getUiEventLogger();
}
protected final void resetStates() {
@@ -358,8 +372,7 @@
* {@link QSTileImpl#getLongClickIntent}
*/
protected void handleLongClick() {
- Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(
- getLongClickIntent(), 0);
+ mActivityStarter.postStartActivityDismissingKeyguard(getLongClickIntent(), 0);
}
/**
@@ -533,7 +546,8 @@
private static final int REMOVE_CALLBACKS = 11;
private static final int REMOVE_CALLBACK = 12;
private static final int SET_LISTENING = 13;
- private static final int STALE = 14;
+ @VisibleForTesting
+ protected static final int STALE = 14;
@VisibleForTesting
protected H(Looper looper) {
@@ -558,8 +572,7 @@
if (mState.disabledByPolicy) {
Intent intent = RestrictedLockUtils.getShowAdminSupportDetailsIntent(
mContext, mEnforcedAdmin);
- Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(
- intent, 0);
+ mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
} else {
handleClick();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index b24fdbf..b848590 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -21,6 +21,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
+import android.os.Handler;
+import android.os.Looper;
import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings.Global;
@@ -33,33 +35,50 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.GlobalSetting;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import javax.inject.Inject;
+import dagger.Lazy;
+
/** Quick settings tile: Airplane mode **/
public class AirplaneModeTile extends QSTileImpl<BooleanState> {
private final Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_airplane);
private final GlobalSetting mSetting;
- private final ActivityStarter mActivityStarter;
private final BroadcastDispatcher mBroadcastDispatcher;
+ private final Lazy<ConnectivityManager> mLazyConnectivityManager;
private boolean mListening;
@Inject
- public AirplaneModeTile(QSHost host, ActivityStarter activityStarter,
- BroadcastDispatcher broadcastDispatcher) {
- super(host);
- mActivityStarter = activityStarter;
+ public AirplaneModeTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ BroadcastDispatcher broadcastDispatcher,
+ Lazy<ConnectivityManager> lazyConnectivityManager
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mBroadcastDispatcher = broadcastDispatcher;
+ mLazyConnectivityManager = lazyConnectivityManager;
mSetting = new GlobalSetting(mContext, mHandler, Global.AIRPLANE_MODE_ON) {
@Override
protected void handleValueChanged(int value) {
+ // mHandler is the background handler so calling this is OK
handleRefreshState(value);
}
};
@@ -83,9 +102,7 @@
}
private void setEnabled(boolean enabled) {
- final ConnectivityManager mgr =
- (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- mgr.setAirplaneMode(enabled);
+ mLazyConnectivityManager.get().setAirplaneMode(enabled);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index f249504..eb794a8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -16,16 +16,24 @@
package com.android.systemui.qs.tiles;
import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings.Secure;
import android.service.quicksettings.Tile;
import android.widget.Switch;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.SecureSetting;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -46,8 +54,18 @@
private Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_battery_saver);
@Inject
- public BatterySaverTile(QSHost host, BatteryController batteryController) {
- super(host);
+ public BatterySaverTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ BatteryController batteryController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mBatteryController = batteryController;
mBatteryController.observe(getLifecycle(), this);
int currentUser = host.getUserContext().getUserId();
@@ -55,6 +73,7 @@
currentUser) {
@Override
protected void handleValueChanged(int value, boolean observedChange) {
+ // mHandler is the background handler so calling this is OK
handleRefreshState(null);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index c67ece0..1424244 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -24,6 +24,8 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
@@ -37,12 +39,16 @@
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSDetailItems;
import com.android.systemui.qs.QSDetailItems.Item;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.BluetoothController;
@@ -58,15 +64,21 @@
private final BluetoothController mController;
private final BluetoothDetailAdapter mDetailAdapter;
- private final ActivityStarter mActivityStarter;
@Inject
- public BluetoothTile(QSHost host,
- BluetoothController bluetoothController,
- ActivityStarter activityStarter) {
- super(host);
+ public BluetoothTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ BluetoothController bluetoothController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mController = bluetoothController;
- mActivityStarter = activityStarter;
mDetailAdapter = (BluetoothDetailAdapter) createDetailAdapter();
mController.observe(getLifecycle(), mCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 4b53ae2..56b939d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -22,6 +22,8 @@
import android.content.Context;
import android.content.Intent;
import android.media.MediaRouter.RouteInfo;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.util.Log;
@@ -35,12 +37,16 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSDetailItems;
import com.android.systemui.qs.QSDetailItems.Item;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.CastController;
@@ -64,20 +70,28 @@
private final KeyguardStateController mKeyguard;
private final NetworkController mNetworkController;
private final Callback mCallback = new Callback();
- private final ActivityStarter mActivityStarter;
private Dialog mDialog;
private boolean mWifiConnected;
@Inject
- public CastTile(QSHost host, CastController castController,
- KeyguardStateController keyguardStateController, NetworkController networkController,
- ActivityStarter activityStarter) {
- super(host);
+ public CastTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ CastController castController,
+ KeyguardStateController keyguardStateController,
+ NetworkController networkController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mController = castController;
mDetailAdapter = new CastDetailAdapter();
mKeyguard = keyguardStateController;
mNetworkController = networkController;
- mActivityStarter = activityStarter;
mController.observe(this, mCallback);
mKeyguard.observe(this, mCallback);
mNetworkController.observe(this, mSignalCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index bc03ca6..b4f1fe7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -23,6 +23,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.telephony.SubscriptionManager;
@@ -39,12 +41,16 @@
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.Prefs;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile.SignalState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.SignalTileView;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.NetworkController;
@@ -62,14 +68,21 @@
private final CellularDetailAdapter mDetailAdapter;
private final CellSignalCallback mSignalCallback = new CellSignalCallback();
- private final ActivityStarter mActivityStarter;
@Inject
- public CellularTile(QSHost host, NetworkController networkController,
- ActivityStarter activityStarter) {
- super(host);
+ public CellularTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ NetworkController networkController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mController = networkController;
- mActivityStarter = activityStarter;
mDataController = mController.getMobileDataController();
mDetailAdapter = new CellularDetailAdapter();
mController.observe(getLifecycle(), mSignalCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index 9c0030d..347ef45 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -17,17 +17,26 @@
package com.android.systemui.qs.tiles;
import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.service.quicksettings.Tile;
import android.widget.Switch;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.R.drawable;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.SecureSetting;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import javax.inject.Inject;
@@ -35,19 +44,33 @@
/** Quick settings tile: Invert colors **/
public class ColorInversionTile extends QSTileImpl<BooleanState> {
+ private static final String EXTRA_FRAGMENT_ARGS_KEY = ":settings:fragment_args_key";
+ private static final String EXTRA_SHOW_FRAGMENT_ARGS_KEY = ":settings:show_fragment_args";
+ private static final String COLOR_INVERSION_PREFERENCE_KEY = "toggle_inversion_preference";
+
private final Icon mIcon = ResourceIcon.get(drawable.ic_invert_colors);
private final SecureSetting mSetting;
private boolean mListening;
@Inject
- public ColorInversionTile(QSHost host) {
- super(host);
+ public ColorInversionTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
- mSetting = new SecureSetting(mContext, mHandler,
+ mSetting = new SecureSetting(mContext, mainHandler,
Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) {
@Override
protected void handleValueChanged(int value, boolean observedChange) {
+ // mHandler is the background handler so calling this is OK
handleRefreshState(value);
}
};
@@ -78,7 +101,11 @@
@Override
public Intent getLongClickIntent() {
- return new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
+ Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
+ Bundle bundle = new Bundle();
+ bundle.putString(EXTRA_FRAGMENT_ARGS_KEY, COLOR_INVERSION_PREFERENCE_KEY);
+ intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGS_KEY, bundle);
+ return intent;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 7ae8fbc..85f1245 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -16,19 +16,26 @@
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.widget.Switch;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Prefs;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.DataSaverController;
-import com.android.systemui.statusbar.policy.NetworkController;
import javax.inject.Inject;
@@ -38,9 +45,19 @@
private final DataSaverController mDataSaverController;
@Inject
- public DataSaverTile(QSHost host, NetworkController networkController) {
- super(host);
- mDataSaverController = networkController.getDataSaverController();
+ public DataSaverTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ DataSaverController dataSaverController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
+ mDataSaverController = dataSaverController;
mDataSaverController.observe(getLifecycle(), this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 9f7b84a..ec8b143 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -30,6 +30,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings.Global;
@@ -53,11 +55,14 @@
import com.android.systemui.R;
import com.android.systemui.SysUIToast;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -79,7 +84,6 @@
private final ZenModeController mController;
private final DndDetailAdapter mDetailAdapter;
- private final ActivityStarter mActivityStarter;
private final SharedPreferences mSharedPreferences;
private final BroadcastDispatcher mBroadcastDispatcher;
@@ -88,12 +92,21 @@
private boolean mReceiverRegistered;
@Inject
- public DndTile(QSHost host, ZenModeController zenModeController,
- ActivityStarter activityStarter, BroadcastDispatcher broadcastDispatcher,
- @Main SharedPreferences sharedPreferences) {
- super(host);
+ public DndTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ ZenModeController zenModeController,
+ BroadcastDispatcher broadcastDispatcher,
+ @Main SharedPreferences sharedPreferences
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mController = zenModeController;
- mActivityStarter = activityStarter;
mSharedPreferences = sharedPreferences;
mDetailAdapter = new DndDetailAdapter();
mBroadcastDispatcher = broadcastDispatcher;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 27ccd7c..cd45082 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -18,14 +18,22 @@
import android.app.ActivityManager;
import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.MediaStore;
import android.service.quicksettings.Tile;
import android.widget.Switch;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.FlashlightController;
@@ -39,8 +47,18 @@
private final FlashlightController mFlashlightController;
@Inject
- public FlashlightTile(QSHost host, FlashlightController flashlightController) {
- super(host);
+ public FlashlightTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ FlashlightController flashlightController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mFlashlightController = flashlightController;
mFlashlightController.observe(getLifecycle(), this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 1ab77f3..a45d94a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -18,16 +18,24 @@
import android.annotation.Nullable;
import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
import android.os.UserManager;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.util.Log;
import android.widget.Switch;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.HotspotController;
@@ -45,9 +53,19 @@
private boolean mListening;
@Inject
- public HotspotTile(QSHost host, HotspotController hotspotController,
- DataSaverController dataSaverController) {
- super(host);
+ public HotspotTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ HotspotController hotspotController,
+ DataSaverController dataSaverController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mHotspotController = hotspotController;
mDataSaverController = dataSaverController;
mHotspotController.observe(this, mCallbacks);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 02f364b..d502d06 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -17,16 +17,23 @@
package com.android.systemui.qs.tiles;
import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
import android.os.UserManager;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.widget.Switch;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.LocationController;
@@ -41,16 +48,24 @@
private final LocationController mController;
private final KeyguardStateController mKeyguard;
- private final ActivityStarter mActivityStarter;
private final Callback mCallback = new Callback();
@Inject
- public LocationTile(QSHost host, LocationController locationController,
- KeyguardStateController keyguardStateController, ActivityStarter activityStarter) {
- super(host);
+ public LocationTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ LocationController locationController,
+ KeyguardStateController keyguardStateController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mController = locationController;
mKeyguard = keyguardStateController;
- mActivityStarter = activityStarter;
mController.observe(this, mCallback);
mKeyguard.observe(this, mCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index 7da9135..fb281169 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -23,15 +23,23 @@
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.nfc.NfcAdapter;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.widget.Switch;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import javax.inject.Inject;
@@ -47,8 +55,18 @@
private boolean mListening;
@Inject
- public NfcTile(QSHost host, BroadcastDispatcher broadcastDispatcher) {
- super(host);
+ public NfcTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ BroadcastDispatcher broadcastDispatcher
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mBroadcastDispatcher = broadcastDispatcher;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index 2f58272..3264429 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -33,10 +33,17 @@
import androidx.annotation.StringRes;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
+import com.android.systemui.dagger.NightDisplayListenerModule;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.LocationController;
@@ -62,15 +69,29 @@
private final ColorDisplayManager mManager;
private final LocationController mLocationController;
+ private final NightDisplayListenerModule.Builder mNightDisplayListenerBuilder;
private NightDisplayListener mListener;
private boolean mIsListening;
@Inject
- public NightDisplayTile(QSHost host, LocationController locationController) {
- super(host);
+ public NightDisplayTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ LocationController locationController,
+ ColorDisplayManager colorDisplayManager,
+ NightDisplayListenerModule.Builder nightDisplayListenerBuilder
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mLocationController = locationController;
- mManager = mContext.getSystemService(ColorDisplayManager.class);
- mListener = new NightDisplayListener(mContext, new Handler(Looper.myLooper()));
+ mManager = colorDisplayManager;
+ mNightDisplayListenerBuilder = nightDisplayListenerBuilder;
+ mListener = mNightDisplayListenerBuilder.setUser(host.getUserContext().getUserId()).build();
}
@Override
@@ -106,7 +127,7 @@
}
// Make a new controller for the new user.
- mListener = new NightDisplayListener(mContext, newUserId, new Handler(Looper.myLooper()));
+ mListener = mNightDisplayListenerBuilder.setUser(newUserId).build();
if (mIsListening) {
mListener.setCallback(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 5dcb4e3..4bf27e2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -19,14 +19,22 @@
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.widget.Switch;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.RotationLockController;
import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
@@ -40,8 +48,18 @@
private final RotationLockController mController;
@Inject
- public RotationLockTile(QSHost host, RotationLockController rotationLockController) {
- super(host);
+ public RotationLockTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ RotationLockController rotationLockController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mController = rotationLockController;
mController.observe(this, mCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 0c34b27..d7a2975 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -17,15 +17,22 @@
package com.android.systemui.qs.tiles;
import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Switch;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
@@ -44,9 +51,19 @@
private Callback mCallback = new Callback();
@Inject
- public ScreenRecordTile(QSHost host, RecordingController controller,
- KeyguardDismissUtil keyguardDismissUtil) {
- super(host);
+ public ScreenRecordTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ RecordingController controller,
+ KeyguardDismissUtil keyguardDismissUtil
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mController = controller;
mController.observe(this, mCallback);
mKeyguardDismissUtil = keyguardDismissUtil;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index 7b83c20..07b841f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -19,15 +19,23 @@
import android.app.UiModeManager;
import android.content.Intent;
import android.content.res.Configuration;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
import android.widget.Switch;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -55,9 +63,20 @@
private final LocationController mLocationController;
@Inject
- public UiModeNightTile(QSHost host, ConfigurationController configurationController,
- BatteryController batteryController, LocationController locationController) {
- super(host);
+ public UiModeNightTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ ConfigurationController configurationController,
+ BatteryController batteryController,
+ LocationController locationController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mBatteryController = batteryController;
mUiModeManager = mContext.getSystemService(UiModeManager.class);
mLocationController = locationController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
index aab30d4..26adfdc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -18,14 +18,22 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.util.Pair;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.State;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -39,9 +47,19 @@
private Pair<String, Drawable> mLastUpdate;
@Inject
- public UserTile(QSHost host, UserSwitcherController userSwitcherController,
- UserInfoController userInfoController) {
- super(host);
+ public UserTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ UserSwitcherController userSwitcherController,
+ UserInfoController userInfoController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mUserSwitcherController = userSwitcherController;
mUserInfoController = userInfoController;
mUserInfoController.observe(getLifecycle(), this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 1279d42..4d89dea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -20,6 +20,8 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
@@ -32,15 +34,19 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.wifi.AccessPoint;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.SignalState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.AlphaControlledSignalTileView;
import com.android.systemui.qs.QSDetailItems;
import com.android.systemui.qs.QSDetailItems.Item;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSIconViewImpl;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.NetworkController;
@@ -63,17 +69,24 @@
private final QSTile.SignalState mStateBeforeClick = newTileState();
protected final WifiSignalCallback mSignalCallback = new WifiSignalCallback();
- private final ActivityStarter mActivityStarter;
private boolean mExpectDisabled;
@Inject
- public WifiTile(QSHost host, NetworkController networkController,
- ActivityStarter activityStarter) {
- super(host);
+ public WifiTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ NetworkController networkController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mController = networkController;
mWifiController = mController.getAccessPointController();
mDetailAdapter = (WifiDetailAdapter) createDetailAdapter();
- mActivityStarter = activityStarter;
mController.observe(getLifecycle(), mSignalCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 318c0c4..5235b6d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -17,14 +17,22 @@
package com.android.systemui.qs.tiles;
import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.widget.Switch;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.phone.ManagedProfileController;
@@ -38,8 +46,18 @@
private final ManagedProfileController mProfileController;
@Inject
- public WorkModeTile(QSHost host, ManagedProfileController managedProfileController) {
- super(host);
+ public WorkModeTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ ManagedProfileController managedProfileController
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
mProfileController = managedProfileController;
mProfileController.observe(getLifecycle(), this);
}
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/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 476ec79..469c4a7 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -42,6 +42,7 @@
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.LongRunning;
import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import java.io.IOException;
import java.util.concurrent.Executor;
@@ -72,7 +73,7 @@
private static final String ACTION_DELETE = "com.android.systemui.screenrecord.DELETE";
private final RecordingController mController;
-
+ private final KeyguardDismissUtil mKeyguardDismissUtil;
private ScreenRecordingAudioSource mAudioSource;
private boolean mShowTaps;
private boolean mOriginalShowTaps;
@@ -85,12 +86,13 @@
@Inject
public RecordingService(RecordingController controller, @LongRunning Executor executor,
UiEventLogger uiEventLogger, NotificationManager notificationManager,
- CurrentUserContextTracker userContextTracker) {
+ CurrentUserContextTracker userContextTracker, KeyguardDismissUtil keyguardDismissUtil) {
mController = controller;
mLongExecutor = executor;
mUiEventLogger = uiEventLogger;
mNotificationManager = notificationManager;
mUserContextTracker = userContextTracker;
+ mKeyguardDismissUtil = keyguardDismissUtil;
}
/**
@@ -170,33 +172,36 @@
Intent shareIntent = new Intent(Intent.ACTION_SEND)
.setType("video/mp4")
.putExtra(Intent.EXTRA_STREAM, shareUri);
- String shareLabel = getResources().getString(R.string.screenrecord_share_label);
+ mKeyguardDismissUtil.executeWhenUnlocked(() -> {
+ String shareLabel = getResources().getString(R.string.screenrecord_share_label);
+ startActivity(Intent.createChooser(shareIntent, shareLabel)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ // Remove notification
+ mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser);
+ return false;
+ }, false);
// Close quick shade
sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
-
- // Remove notification
- mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser);
-
- startActivity(Intent.createChooser(shareIntent, shareLabel)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
break;
case ACTION_DELETE:
- // Close quick shade
- sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+ mKeyguardDismissUtil.executeWhenUnlocked(() -> {
+ // Close quick shade
+ sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+ ContentResolver resolver = getContentResolver();
+ Uri uri = Uri.parse(intent.getStringExtra(EXTRA_PATH));
+ resolver.delete(uri, null, null);
- ContentResolver resolver = getContentResolver();
- Uri uri = Uri.parse(intent.getStringExtra(EXTRA_PATH));
- resolver.delete(uri, null, null);
+ Toast.makeText(
+ this,
+ R.string.screenrecord_delete_description,
+ Toast.LENGTH_LONG).show();
+ Log.d(TAG, "Deleted recording " + uri);
- Toast.makeText(
- this,
- R.string.screenrecord_delete_description,
- Toast.LENGTH_LONG).show();
-
- // Remove notification
- mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser);
- Log.d(TAG, "Deleted recording " + uri);
+ // Remove notification
+ mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser);
+ return false;
+ }, false);
break;
}
return Service.START_STICKY;
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 8544430..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;
@@ -57,13 +52,11 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.PowerManager;
import android.os.RemoteException;
import android.provider.Settings;
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;
@@ -88,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
*/
@@ -193,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;
@@ -248,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;
@@ -505,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;
@@ -648,14 +636,6 @@
*/
private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets,
boolean showFlash) {
-
- // If power save is on, show a toast so there is some visual indication that a
- // screenshot has been taken.
- PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- if (powerManager.isPowerSaveMode()) {
- Toast.makeText(mContext, R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show();
- }
-
mScreenshotHandler.post(() -> {
if (!mScreenshotLayout.isAttachedToWindow()) {
mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
@@ -713,7 +693,7 @@
});
}
- mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data);
+ mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
mSaveInBgTask.execute();
}
@@ -1033,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);
@@ -1125,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/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 570a4bb..eb72312 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -39,16 +39,16 @@
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
-import com.android.systemui.TransactionPool;
import com.android.systemui.recents.Recents;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.wm.DisplayChangeController;
-import com.android.systemui.wm.DisplayController;
-import com.android.systemui.wm.DisplayImeController;
-import com.android.systemui.wm.DisplayLayout;
-import com.android.systemui.wm.SystemWindows;
+import com.android.wm.shell.common.DisplayChangeController;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.TransactionPool;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
index 5aeca5e..84ec387 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
@@ -33,8 +33,8 @@
import androidx.annotation.Nullable;
-import com.android.systemui.TransactionPool;
-import com.android.systemui.wm.DisplayImeController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.TransactionPool;
class DividerImeController implements DisplayImeController.ImePositionProcessor {
private static final String TAG = "DividerImeController";
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
index 3b7f315..c24431c 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
@@ -19,13 +19,13 @@
import android.content.Context;
import android.os.Handler;
-import com.android.systemui.TransactionPool;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.recents.Recents;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.wm.DisplayController;
-import com.android.systemui.wm.DisplayImeController;
-import com.android.systemui.wm.SystemWindows;
+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 java.util.Optional;
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
index 6ea3132..d869333 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
@@ -32,7 +32,7 @@
import android.view.View;
import android.view.WindowManager;
-import com.android.systemui.wm.SystemWindows;
+import com.android.wm.shell.common.SystemWindows;
/**
* Manages the window parameters of the docked stack divider.
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java
index 69095f7..a34e85517 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java
@@ -34,7 +34,7 @@
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DockedDividerUtils;
-import com.android.systemui.wm.DisplayLayout;
+import com.android.wm.shell.common.DisplayLayout;
/**
* Handles split-screen related internal display layout. In general, this represents the
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java
index 1ff4046..6812f62 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java
@@ -25,7 +25,7 @@
import androidx.annotation.NonNull;
-import com.android.systemui.TransactionPool;
+import com.android.wm.shell.common.TransactionPool;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 410e3dd..2b36812 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -40,7 +40,7 @@
import android.window.WindowOrganizer;
import com.android.internal.annotations.GuardedBy;
-import com.android.systemui.TransactionPool;
+import com.android.wm.shell.common.TransactionPool;
import java.util.ArrayList;
import java.util.List;
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/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 9d81d35..a86ab41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -842,7 +842,7 @@
}
private static final NotifSection sDefaultSection =
- new NotifSection("DefaultSection") {
+ new NotifSection("UnknownSection") {
@Override
public boolean isInSection(ListEntry entry) {
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
index 4b244bb..68ec6b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+
import android.app.Notification;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
@@ -24,9 +26,11 @@
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.appops.AppOpsController;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.util.Assert;
@@ -43,6 +47,8 @@
* Tags notifications with appOps
* Lifetime extends notifications associated with an ongoing ForegroundService.
* Filters out notifications that represent foreground services that are no longer running
+ * Puts foreground service notifications into the FGS section. See {@link NotifCoordinators} for
+ * section ordering priority.
*
* Previously this logic lived in
* frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceController
@@ -86,6 +92,10 @@
mAppOpsController.addCallback(ForegroundServiceController.APP_OPS, this::onAppOpsChanged);
}
+ public NotifSection getSection() {
+ return mNotifSection;
+ }
+
/**
* Filters out notifications that represent foreground services that are no longer running or
* that already have an app notification with the appOps tagged to
@@ -204,6 +214,23 @@
}
};
+ /**
+ * Puts foreground service notifications into its own section.
+ */
+ private final NotifSection mNotifSection = new NotifSection("ForegroundService") {
+ @Override
+ public boolean isInSection(ListEntry entry) {
+ NotificationEntry notificationEntry = entry.getRepresentativeEntry();
+ if (notificationEntry != null) {
+ Notification notification = notificationEntry.getSbn().getNotification();
+ return notification.isForegroundService()
+ && notification.isColorized()
+ && entry.getRepresentativeEntry().getImportance() > IMPORTANCE_MIN;
+ }
+ return false;
+ }
+ };
+
private void onAppOpsChanged(int code, int uid, String packageName, boolean active) {
mMainExecutor.execute(() -> handleAppOpsChanged(code, uid, packageName, active));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
index 1bac938..1a9de88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
@@ -16,17 +16,25 @@
package com.android.systemui.statusbar.notification.collection.coordinator
+import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
import javax.inject.Inject
import javax.inject.Singleton
/**
- * A coordinator that elevates important conversation notifications
+ * A Conversation/People Coordinator that:
+ * - Elevates important conversation notifications
+ * - Puts conversations into its own people section. @see [NotifCoordinators] for section ordering.
*/
@Singleton
-class ConversationCoordinator @Inject constructor() : Coordinator {
+class ConversationCoordinator @Inject constructor(
+ private val peopleNotificationIdentifier: PeopleNotificationIdentifier
+) : Coordinator {
private val notificationPromoter = object : NotifPromoter(TAG) {
override fun shouldPromoteToTopLevel(entry: NotificationEntry): Boolean {
@@ -34,10 +42,24 @@
}
}
+ private val mNotifSection: NotifSection = object : NotifSection("People") {
+ override fun isInSection(entry: ListEntry): Boolean {
+ return isConversation(entry.representativeEntry!!)
+ }
+ }
+
override fun attach(pipeline: NotifPipeline) {
pipeline.addPromoter(notificationPromoter)
}
+ fun getSection(): NotifSection {
+ return mNotifSection
+ }
+
+ private fun isConversation(entry: NotificationEntry): Boolean =
+ peopleNotificationIdentifier.getPeopleNotificationType(entry.sbn, entry.ranking) !=
+ TYPE_NON_PERSON
+
companion object {
private const val TAG = "ConversationCoordinator"
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java
index d8b2e40..c1a11b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
/**
@@ -29,8 +28,4 @@
* Coordinators should register their listeners and {@link Pluggable}s to the pipeline.
*/
void attach(NotifPipeline pipeline);
-
- default NotifSection getSection() {
- return null;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
index 3fde2ed..72597af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
@@ -88,7 +88,6 @@
pipeline.addNotificationLifetimeExtender(mLifetimeExtender);
}
- @Override
public NotifSection getSection() {
return mNotifSection;
}
@@ -192,7 +191,7 @@
}
};
- private final NotifSection mNotifSection = new NotifSection(TAG) {
+ private final NotifSection mNotifSection = new NotifSection("HeadsUp") {
@Override
public boolean isInSection(ListEntry entry) {
return isCurrentlyShowingHun(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index 99e822c..a09c650 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -60,6 +60,7 @@
PreparationCoordinator preparationCoordinator,
MediaCoordinator mediaCoordinator) {
dumpManager.registerDumpable(TAG, this);
+
mCoordinators.add(new HideLocallyDismissedNotifsCoordinator());
mCoordinators.add(hideNotifsForOtherUsersCoordinator);
mCoordinators.add(keyguardCoordinator);
@@ -67,20 +68,22 @@
mCoordinators.add(appOpsCoordinator);
mCoordinators.add(deviceProvisionedCoordinator);
mCoordinators.add(bubbleCoordinator);
+ mCoordinators.add(mediaCoordinator);
+ mCoordinators.add(conversationCoordinator);
if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
- mCoordinators.add(conversationCoordinator);
mCoordinators.add(headsUpCoordinator);
mCoordinators.add(preparationCoordinator);
}
- // TODO: add new Coordinators here! (b/112656837)
- mCoordinators.add(mediaCoordinator);
- // TODO: add the sections in a particular ORDER (HeadsUp < People < Alerting)
- for (Coordinator c : mCoordinators) {
- if (c.getSection() != null) {
- mOrderedSections.add(c.getSection());
- }
+ // Manually add Ordered Sections
+ // HeadsUp > FGS > People > Alerting > Silent > Unknown/Default
+ if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
+ mOrderedSections.add(headsUpCoordinator.getSection()); // HeadsUp
}
+ mOrderedSections.add(appOpsCoordinator.getSection()); // ForegroundService
+ mOrderedSections.add(conversationCoordinator.getSection()); // People
+ mOrderedSections.add(rankingCoordinator.getAlertingSection()); // Alerting
+ mOrderedSections.add(rankingCoordinator.getSilentSection()); // Silent
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index e9cbf32..0d2f9da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -17,15 +17,19 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* Filters out NotificationEntries based on its Ranking and dozing state.
+ * Assigns alerting / silent section based on the importance of the notification entry.
* We check the NotificationEntry's Ranking for:
* - whether the notification's app is suspended or hiding its notifications
* - whether DND settings are hiding notifications from ambient display or the notification list
@@ -35,10 +39,14 @@
private static final String TAG = "RankingNotificationCoordinator";
private final StatusBarStateController mStatusBarStateController;
+ private final HighPriorityProvider mHighPriorityProvider;
@Inject
- public RankingCoordinator(StatusBarStateController statusBarStateController) {
+ public RankingCoordinator(
+ StatusBarStateController statusBarStateController,
+ HighPriorityProvider highPriorityProvider) {
mStatusBarStateController = statusBarStateController;
+ mHighPriorityProvider = highPriorityProvider;
}
@Override
@@ -49,6 +57,28 @@
pipeline.addPreGroupFilter(mDozingFilter);
}
+ public NotifSection getAlertingSection() {
+ return mAlertingNotifSection;
+ }
+
+ public NotifSection getSilentSection() {
+ return mSilentNotifSection;
+ }
+
+ private final NotifSection mAlertingNotifSection = new NotifSection("Alerting") {
+ @Override
+ public boolean isInSection(ListEntry entry) {
+ return mHighPriorityProvider.isHighPriority(entry);
+ }
+ };
+
+ private final NotifSection mSilentNotifSection = new NotifSection("Silent") {
+ @Override
+ public boolean isInSection(ListEntry entry) {
+ return !mHighPriorityProvider.isHighPriority(entry);
+ }
+ };
+
/**
* Checks whether to filter out the given notification based the notification's Ranking object.
* NotifListBuilder invalidates the notification list each time the ranking is updated,
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/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index d7d09e0..d3b8a8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -34,6 +34,8 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import java.lang.ref.WeakReference;
+
class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeActionHelper {
@VisibleForTesting
@@ -47,7 +49,11 @@
private static final long SWIPE_MENU_TIMING = 200;
- private NotificationMenuRowPlugin mCurrMenuRow;
+ // Hold a weak ref to the menu row so that it isn't accidentally retained in memory. The
+ // lifetime of the row should be the same as the ActivatableView, which is owned by the
+ // NotificationStackScrollLayout. If the notification isn't in the notification shade, then it
+ // isn't possible to swipe it and, so, this class doesn't need to "help."
+ private WeakReference<NotificationMenuRowPlugin> mCurrMenuRowRef;
private boolean mIsExpanded;
private boolean mPulsing;
@@ -82,11 +88,17 @@
return mMenuExposedView;
}
- public void setCurrentMenuRow(NotificationMenuRowPlugin menuRow) {
- mCurrMenuRow = menuRow;
+ @VisibleForTesting
+ void setCurrentMenuRow(NotificationMenuRowPlugin menuRow) {
+ mCurrMenuRowRef = menuRow != null ? new WeakReference(menuRow) : null;
}
- public NotificationMenuRowPlugin getCurrentMenuRow() { return mCurrMenuRow; }
+ public NotificationMenuRowPlugin getCurrentMenuRow() {
+ if (mCurrMenuRowRef == null) {
+ return null;
+ }
+ return mCurrMenuRowRef.get();
+ }
@VisibleForTesting
protected Handler getHandler() { return mHandler; }
@@ -102,8 +114,9 @@
@Override
protected void onChildSnappedBack(View animView, float targetLeft) {
- if (mCurrMenuRow != null && targetLeft == 0) {
- mCurrMenuRow.resetMenu();
+ final NotificationMenuRowPlugin menuRow = getCurrentMenuRow();
+ if (menuRow != null && targetLeft == 0) {
+ menuRow.resetMenu();
clearCurrentMenuRow();
}
}
@@ -129,10 +142,11 @@
@VisibleForTesting
protected void initializeRow(SwipeableView row) {
if (row.hasFinishedInitialization()) {
- mCurrMenuRow = row.createMenu();
- if (mCurrMenuRow != null) {
- mCurrMenuRow.setMenuClickListener(mMenuListener);
- mCurrMenuRow.onTouchStart();
+ final NotificationMenuRowPlugin menuRow = row.createMenu();
+ setCurrentMenuRow(menuRow);
+ if (menuRow != null) {
+ menuRow.setMenuClickListener(mMenuListener);
+ menuRow.onTouchStart();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java
index 5bc17f5..53b369c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java
@@ -50,12 +50,12 @@
/**
* Reload the drawable from resource id, should reapply the previous dark intensity.
*/
- public void updateIcon() {
+ public void updateIcon(int lightIconColor, int darkIconColor) {
if (getCurrentView() == null || !getCurrentView().isAttachedToWindow() || mIconResId == 0) {
return;
}
final KeyButtonDrawable currentDrawable = getImageDrawable();
- KeyButtonDrawable drawable = getNewDrawable();
+ KeyButtonDrawable drawable = getNewDrawable(lightIconColor, darkIconColor);
if (currentDrawable != null) {
drawable.setDarkIntensity(currentDrawable.getDarkIntensity());
}
@@ -116,9 +116,9 @@
mGroup = group;
}
- protected KeyButtonDrawable getNewDrawable() {
- return KeyButtonDrawable.create(getContext().getApplicationContext(), mIconResId,
- false /* shadow */);
+ protected KeyButtonDrawable getNewDrawable(int lightIconColor, int darkIconColor) {
+ return KeyButtonDrawable.create(getContext().getApplicationContext(), lightIconColor,
+ darkIconColor, mIconResId, false /* shadow */, null /* ovalBackground */);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java
index 9e843f9..c1017f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java
@@ -111,9 +111,9 @@
* Update all the icons that are attached to this group. This will get all the buttons to update
* their icons for their buttons.
*/
- public void updateIcons() {
+ public void updateIcons(int lightIconColor, int darkIconColor) {
for (ButtonData data : mButtonData) {
- data.button.updateIcon();
+ data.button.updateIcon(lightIconColor, darkIconColor);
}
}
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/FloatingRotationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
index 16b5a23..687f5f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
@@ -16,19 +16,16 @@
package com.android.systemui.statusbar.phone;
-import android.annotation.ColorInt;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.PixelFormat;
-import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
-import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
import com.android.systemui.statusbar.policy.KeyButtonView;
@@ -65,6 +62,8 @@
@Override
public void setRotationButtonController(RotationButtonController rotationButtonController) {
mRotationButtonController = rotationButtonController;
+ updateIcon(mRotationButtonController.getLightIconColor(),
+ mRotationButtonController.getDarkIconColor());
}
@Override
@@ -101,7 +100,6 @@
default:
break;
}
- updateIcon();
mWindowManager.addView(mKeyButtonView, lp);
if (mKeyButtonDrawable != null && mKeyButtonDrawable.canAnimate()) {
mKeyButtonDrawable.resetAnimation();
@@ -126,17 +124,13 @@
}
@Override
- public void updateIcon() {
- if (!mIsShowing) {
- return;
- }
- mKeyButtonDrawable = getImageDrawable();
+ public void updateIcon(int lightIconColor, int darkIconColor) {
+ Color ovalBackgroundColor = Color.valueOf(Color.red(darkIconColor),
+ Color.green(darkIconColor), Color.blue(darkIconColor), BACKGROUND_ALPHA);
+ mKeyButtonDrawable = KeyButtonDrawable.create(mRotationButtonController.getContext(),
+ lightIconColor, darkIconColor, mRotationButtonController.getIconResId(),
+ false /* shadow */, ovalBackgroundColor);
mKeyButtonView.setImageDrawable(mKeyButtonDrawable);
- mKeyButtonDrawable.setCallback(mKeyButtonView);
- if (mKeyButtonDrawable != null && mKeyButtonDrawable.canAnimate()) {
- mKeyButtonDrawable.resetAnimation();
- mKeyButtonDrawable.startAnimation();
- }
}
@Override
@@ -151,20 +145,7 @@
@Override
public KeyButtonDrawable getImageDrawable() {
- Context context = new ContextThemeWrapper(mContext.getApplicationContext(),
- mRotationButtonController.getStyleRes());
- final int dualToneDarkTheme = Utils.getThemeAttr(context, R.attr.darkIconTheme);
- final int dualToneLightTheme = Utils.getThemeAttr(context, R.attr.lightIconTheme);
- Context lightContext = new ContextThemeWrapper(context, dualToneLightTheme);
- Context darkContext = new ContextThemeWrapper(context, dualToneDarkTheme);
- @ColorInt int darkColor = Utils.getColorAttrDefaultColor(darkContext,
- R.attr.singleToneColor);
- Color ovalBackgroundColor = Color.valueOf(Color.red(darkColor), Color.green(darkColor),
- Color.blue(darkColor), BACKGROUND_ALPHA);
-
- return KeyButtonDrawable.create(lightContext,
- Utils.getColorAttrDefaultColor(lightContext, R.attr.singleToneColor), darkColor,
- R.drawable.ic_sysbar_rotate_button, false /* shadow */, ovalBackgroundColor);
+ return mKeyButtonDrawable;
}
@Override
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..5266e25 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
@@ -1422,6 +1428,9 @@
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ if (mNavigationBarView == null) {
+ return;
+ }
String action = intent.getAction();
if (Intent.ACTION_SCREEN_OFF.equals(action)
|| Intent.ACTION_SCREEN_ON.equals(action)) {
@@ -1458,11 +1467,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 +1481,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 1eab427..84512ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -48,6 +48,7 @@
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
+import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.MotionEvent;
import android.view.Surface;
@@ -63,6 +64,7 @@
import android.widget.FrameLayout;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -113,17 +115,16 @@
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;
private KeyButtonDrawable mRecentIcon;
private KeyButtonDrawable mDockedIcon;
+ private Context mLightContext;
+ private int mLightIconColor;
+ private int mDarkIconColor;
private EdgeBackGestureHandler mEdgeBackGestureHandler;
private final DeadZone mDeadZone;
@@ -278,6 +279,12 @@
public NavigationBarView(Context context, AttributeSet attrs) {
super(context, attrs);
+ final Context darkContext = new ContextThemeWrapper(context,
+ Utils.getThemeAttr(context, R.attr.darkIconTheme));
+ mLightContext = new ContextThemeWrapper(context,
+ Utils.getThemeAttr(context, R.attr.lightIconTheme));
+ mLightIconColor = Utils.getColorAttrDefaultColor(mLightContext, R.attr.singleToneColor);
+ mDarkIconColor = Utils.getColorAttrDefaultColor(darkContext, R.attr.singleToneColor);
mIsVertical = false;
mLongClickableAccessibilityButton = false;
mNavBarMode = Dependency.get(NavigationModeController.class).addListener(this);
@@ -290,7 +297,7 @@
final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher,
R.drawable.ic_ime_switcher_default);
final RotationContextButton rotateSuggestionButton = new RotationContextButton(
- R.id.rotate_suggestion, R.drawable.ic_sysbar_rotate_button);
+ R.id.rotate_suggestion, R.drawable.ic_sysbar_rotate_button_ccw_start_0);
final ContextualButton accessibilityButton =
new ContextualButton(R.id.accessibility_button,
R.drawable.ic_sysbar_accessibility_button);
@@ -303,8 +310,8 @@
mOverviewProxyService = Dependency.get(OverviewProxyService.class);
mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
mFloatingRotationButton = new FloatingRotationButton(context);
- mRotationButtonController = new RotationButtonController(context,
- R.style.RotateButtonCCWStart90,
+ mRotationButtonController = new RotationButtonController(mLightContext,
+ mLightIconColor, mDarkIconColor,
isGesturalMode ? mFloatingRotationButton : rotateSuggestionButton);
mConfiguration = new Configuration();
@@ -501,7 +508,7 @@
}
if (densityChange || dirChange) {
mRecentIcon = getDrawable(R.drawable.ic_sysbar_recent);
- mContextualButtonGroup.updateIcons();
+ mContextualButtonGroup.updateIcons(mLightIconColor, mDarkIconColor);
}
if (orientationChange || densityChange || dirChange) {
mBackIcon = getBackDrawable();
@@ -559,11 +566,6 @@
drawable.setRotation(mIsVertical ? 90 : 0);
}
- private KeyButtonDrawable chooseNavigationIconDrawable(@DrawableRes int icon,
- @DrawableRes int quickStepIcon) {
- return getDrawable(chooseNavigationIconDrawableRes(icon, quickStepIcon));
- }
-
private @DrawableRes int chooseNavigationIconDrawableRes(@DrawableRes int icon,
@DrawableRes int quickStepIcon) {
final boolean quickStepEnabled = mOverviewProxyService.shouldShowSwipeUpUI();
@@ -571,11 +573,8 @@
}
private KeyButtonDrawable getDrawable(@DrawableRes int icon) {
- return KeyButtonDrawable.create(mContext, icon, true /* hasShadow */);
- }
-
- private KeyButtonDrawable getDrawable(@DrawableRes int icon, boolean hasShadow) {
- return KeyButtonDrawable.create(mContext, icon, hasShadow);
+ return KeyButtonDrawable.create(mLightContext, mLightIconColor, mDarkIconColor, icon,
+ true /* hasShadow */, null /* ovalBackgroundColor */);
}
/** To be called when screen lock/unlock state changes */
@@ -709,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
@@ -861,7 +861,6 @@
mBarTransitions.onNavigationModeChanged(mNavBarMode);
mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode);
mRecentsOnboarding.onNavigationModeChanged(mNavBarMode);
- getRotateSuggestionButton().onNavigationModeChanged(mNavBarMode);
if (isGesturalMode(mNavBarMode)) {
mRegionSamplingHelper.start(mSamplingBounds);
@@ -927,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/RotationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButton.java
index 2580c0e..687efd3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButton.java
@@ -27,7 +27,7 @@
boolean show();
boolean hide();
boolean isVisible();
- void updateIcon();
+ void updateIcon(int lightIconColor, int darkIconColor);
void setOnClickListener(View.OnClickListener onClickListener);
void setOnHoverListener(View.OnHoverListener onHoverListener);
KeyButtonDrawable getImageDrawable();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java
index 59b10e4..f83cdd4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java
@@ -21,6 +21,8 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
+import android.annotation.ColorInt;
+import android.annotation.DrawableRes;
import android.annotation.StyleRes;
import android.app.StatusBarManager;
import android.content.ContentResolver;
@@ -30,6 +32,7 @@
import android.os.RemoteException;
import android.provider.Settings;
import android.util.Log;
+import android.view.ContextThemeWrapper;
import android.view.IRotationWatcher.Stub;
import android.view.MotionEvent;
import android.view.Surface;
@@ -40,6 +43,7 @@
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
+import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -61,10 +65,12 @@
private static final int NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION = 3;
+ private final Context mContext;
+ private final RotationButton mRotationButton;
+ private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
private final UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
private final ViewRippler mViewRippler = new ViewRippler();
- private @StyleRes int mStyleRes;
private int mLastRotationSuggestion;
private boolean mPendingRotationSuggestion;
private boolean mHoveringRotationSuggestion;
@@ -75,6 +81,9 @@
private boolean mListenersRegistered = false;
private boolean mIsNavigationBarShowing;
private boolean mSkipOverrideUserLockPrefsOnce;
+ private int mLightIconColor;
+ private int mDarkIconColor;
+ private int mIconResId = R.drawable.ic_sysbar_rotate_button_ccw_start_90;
private final Runnable mRemoveRotationProposal =
() -> setRotateSuggestionButtonState(false /* visible */);
@@ -82,9 +91,6 @@
() -> mPendingRotationSuggestion = false;
private Animator mRotateHideAnimator;
- private final Context mContext;
- private final RotationButton mRotationButton;
- private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
private final Stub mRotationWatcher = new Stub() {
@Override
@@ -117,12 +123,14 @@
return (disable2Flags & StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS) != 0;
}
- RotationButtonController(Context context, @StyleRes int style, RotationButton rotationButton) {
+ RotationButtonController(Context context, @ColorInt int lightIconColor,
+ @ColorInt int darkIconColor, RotationButton rotationButton) {
mContext = context;
+ mLightIconColor = lightIconColor;
+ mDarkIconColor = darkIconColor;
mRotationButton = rotationButton;
mRotationButton.setRotationButtonController(this);
- mStyleRes = style;
mIsNavigationBarShowing = true;
mRotationLockController = Dependency.get(RotationLockController.class);
mAccessibilityManagerWrapper = Dependency.get(AccessibilityManagerWrapper.class);
@@ -275,17 +283,20 @@
return;
}
+ // TODO: Remove styles?
// Prepare to show the navbar icon by updating the icon style to change anim params
mLastRotationSuggestion = rotation; // Remember rotation for click
final boolean rotationCCW = isRotationAnimationCCW(windowRotation, rotation);
- int style;
if (windowRotation == Surface.ROTATION_0 || windowRotation == Surface.ROTATION_180) {
- style = rotationCCW ? R.style.RotateButtonCCWStart90 : R.style.RotateButtonCWStart90;
+ mIconResId = rotationCCW
+ ? R.drawable.ic_sysbar_rotate_button_ccw_start_90
+ : R.drawable.ic_sysbar_rotate_button_cw_start_90;
} else { // 90 or 270
- style = rotationCCW ? R.style.RotateButtonCCWStart0 : R.style.RotateButtonCWStart0;
+ mIconResId = rotationCCW
+ ? R.drawable.ic_sysbar_rotate_button_ccw_start_0
+ : R.drawable.ic_sysbar_rotate_button_ccw_start_0;
}
- mStyleRes = style;
- mRotationButton.updateIcon();
+ mRotationButton.updateIcon(mLightIconColor, mDarkIconColor);
if (mIsNavigationBarShowing) {
// The navbar is visible so show the icon right away
@@ -316,14 +327,26 @@
}
}
- @StyleRes int getStyleRes() {
- return mStyleRes;
+ Context getContext() {
+ return mContext;
}
RotationButton getRotationButton() {
return mRotationButton;
}
+ @DrawableRes int getIconResId() {
+ return mIconResId;
+ }
+
+ @ColorInt int getLightIconColor() {
+ return mLightIconColor;
+ }
+
+ @ColorInt int getDarkIconColor() {
+ return mDarkIconColor;
+ }
+
private void onRotateSuggestionClick(View v) {
mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_ACCEPTED);
incrementNumAcceptedRotationSuggestionsIfNeeded();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
index bd96752..d7e95e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
@@ -16,22 +16,16 @@
package com.android.systemui.statusbar.phone;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
-
import android.annotation.DrawableRes;
import android.annotation.IdRes;
-import android.content.Context;
-import android.view.ContextThemeWrapper;
import android.view.View;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
/** Containing logic for the rotation button in nav bar. */
-public class RotationContextButton extends ContextualButton implements
- NavigationModeController.ModeChangedListener, RotationButton {
+public class RotationContextButton extends ContextualButton implements RotationButton {
public static final boolean DEBUG_ROTATION = false;
- private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
private RotationButtonController mRotationButtonController;
public RotationContextButton(@IdRes int buttonResId, @DrawableRes int iconResId) {
@@ -56,16 +50,10 @@
}
@Override
- protected KeyButtonDrawable getNewDrawable() {
- Context context = new ContextThemeWrapper(getContext().getApplicationContext(),
- mRotationButtonController.getStyleRes());
- return KeyButtonDrawable.create(context, mIconResId, false /* shadow */,
- null /* ovalBackgroundColor */);
- }
-
- @Override
- public void onNavigationModeChanged(int mode) {
- mNavBarMode = mode;
+ protected KeyButtonDrawable getNewDrawable(int lightIconColor, int darkIconColor) {
+ return KeyButtonDrawable.create(mRotationButtonController.getContext(),
+ lightIconColor, darkIconColor, mRotationButtonController.getIconResId(),
+ false /* shadow */, null /* ovalBackgroundColor */);
}
@Override
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/KeyButtonDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
index 23d03a4..7559388 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
@@ -37,7 +37,6 @@
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.util.FloatProperty;
-import android.view.ContextThemeWrapper;
import android.view.View;
import com.android.settingslib.Utils;
@@ -439,34 +438,6 @@
}
/**
- * Creates a KeyButtonDrawable with a shadow given its icon. The tint applied to the drawable
- * is determined by the dark and light theme given by the context.
- * @param ctx Context to get the drawable and determine the dark and light theme
- * @param icon the icon resource id
- * @param hasShadow if a shadow will appear with the drawable
- * @param ovalBackgroundColor the color of the oval bg that will be drawn
- * @return KeyButtonDrawable
- */
- public static KeyButtonDrawable create(@NonNull Context ctx, @DrawableRes int icon,
- boolean hasShadow, Color ovalBackgroundColor) {
- final int dualToneDarkTheme = Utils.getThemeAttr(ctx, R.attr.darkIconTheme);
- final int dualToneLightTheme = Utils.getThemeAttr(ctx, R.attr.lightIconTheme);
- Context lightContext = new ContextThemeWrapper(ctx, dualToneLightTheme);
- Context darkContext = new ContextThemeWrapper(ctx, dualToneDarkTheme);
- return KeyButtonDrawable.create(lightContext, darkContext, icon, hasShadow,
- ovalBackgroundColor);
- }
-
- /**
- * Creates a KeyButtonDrawable with a shadow given its icon. For more information, see
- * {@link #create(Context, int, boolean, boolean)}.
- */
- public static KeyButtonDrawable create(@NonNull Context ctx, @DrawableRes int icon,
- boolean hasShadow) {
- return create(ctx, icon, hasShadow, null /* ovalBackgroundColor */);
- }
-
- /**
* Creates a KeyButtonDrawable with a shadow given its icon. For more information, see
* {@link #create(Context, int, boolean, boolean)}.
*/
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/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index b12224b..d1805af 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -44,13 +44,17 @@
import android.util.Log;
import android.util.LongSparseArray;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import java.io.FileDescriptor;
@@ -396,15 +400,23 @@
public static final String TILE_SPEC = "dbg:mem";
private final GarbageMonitor gm;
- private final ActivityStarter mActivityStarter;
private ProcessMemInfo pmi;
private boolean dumpInProgress;
@Inject
- public MemoryTile(QSHost host, GarbageMonitor monitor, ActivityStarter starter) {
- super(host);
+ public MemoryTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ GarbageMonitor monitor
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
gm = monitor;
- mActivityStarter = starter;
}
@Override
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
new file mode 100644
index 0000000..fbc1676
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.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.systemui.wmshell;
+
+import android.content.Context;
+import android.os.Handler;
+import android.view.IWindowManager;
+
+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.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Provides dependencies from {@link com.android.wm.shell}.
+ */
+@Module
+// TODO(b/161116823) Clean up dependencies after wm shell migration finished.
+public class WindowManagerShellModule {
+ @Singleton
+ @Provides
+ static TransactionPool provideTransactionPool() {
+ return new TransactionPool();
+ }
+
+ @Singleton
+ @Provides
+ static DisplayController provideDisplayController(Context context, @Main Handler handler,
+ IWindowManager wmService) {
+ return new DisplayController(context, handler, wmService);
+ }
+
+ @Singleton
+ @Provides
+ static SystemWindows provideSystemWindows(DisplayController displayController,
+ IWindowManager wmService) {
+ return new SystemWindows(displayController, wmService);
+ }
+
+ @Singleton
+ @Provides
+ static DisplayImeController provideDisplayImeController(
+ IWindowManager wmService, DisplayController displayController,
+ @Main Handler mainHandler, TransactionPool 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 cf77850..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 dd3a785..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));
@@ -140,6 +142,10 @@
return null;
}
+ protected FakeBroadcastDispatcher getFakeBroadcastDispatcher() {
+ return mFakeBroadcastDispatcher;
+ }
+
public SysuiTestableContext getContext() {
return mContext;
}
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/classifier/FalsingManagerProxyTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
index ae73879..c3c9ecc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
@@ -29,8 +29,8 @@
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.brightline.BrightLineFalsingManager;
+import com.android.systemui.classifier.brightline.FalsingDataProvider;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerFake;
import com.android.systemui.dump.DumpManager;
@@ -42,6 +42,8 @@
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.utils.leaks.FakeBatteryController;
+import com.android.systemui.utils.leaks.LeakCheckedTest;
import org.junit.After;
import org.junit.Before;
@@ -52,7 +54,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-public class FalsingManagerProxyTest extends SysuiTestCase {
+public class FalsingManagerProxyTest extends LeakCheckedTest {
@Mock(stubOnly = true)
PluginManager mPluginManager;
@Mock(stubOnly = true)
@@ -62,7 +64,7 @@
@Mock DumpManager mDumpManager;
private FalsingManagerProxy mProxy;
private DeviceConfigProxy mDeviceConfig;
- private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+ private FalsingDataProvider mFalsingDataProvider;
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private DockManager mDockManager = new DockManagerFake();
@@ -75,6 +77,8 @@
mDeviceConfig = new DeviceConfigProxyFake();
mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
BRIGHTLINE_FALSING_MANAGER_ENABLED, "false", false);
+ mFalsingDataProvider = new FalsingDataProvider(
+ new DisplayMetrics(), new FakeBatteryController(getLeakCheck()));
}
@After
@@ -86,9 +90,9 @@
@Test
public void test_brightLineFalsingManagerDisabled() {
- mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics,
+ mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor,
mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
- mDumpManager, mUiBgExecutor, mStatusBarStateController);
+ mDumpManager, mUiBgExecutor, mStatusBarStateController, mFalsingDataProvider);
assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
}
@@ -97,17 +101,17 @@
mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false);
mExecutor.runAllReady();
- mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics,
+ mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor,
mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
- mDumpManager, mUiBgExecutor, mStatusBarStateController);
+ mDumpManager, mUiBgExecutor, mStatusBarStateController, mFalsingDataProvider);
assertThat(mProxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class));
}
@Test
public void test_brightLineFalsingManagerToggled() throws InterruptedException {
- mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics,
+ mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor,
mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
- mDumpManager, mUiBgExecutor, mStatusBarStateController);
+ mDumpManager, mUiBgExecutor, mStatusBarStateController, mFalsingDataProvider);
assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
index 2f05f0b..061664b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.classifier.brightline;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
@@ -27,7 +28,6 @@
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerFake;
import com.android.systemui.statusbar.StatusBarState;
@@ -37,6 +37,8 @@
import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.sensors.ThresholdSensor;
+import com.android.systemui.utils.leaks.FakeBatteryController;
+import com.android.systemui.utils.leaks.LeakCheckedTest;
import org.junit.Before;
import org.junit.Test;
@@ -47,7 +49,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class BrightLineFalsingManagerTest extends SysuiTestCase {
+public class BrightLineFalsingManagerTest extends LeakCheckedTest {
@Mock
@@ -55,23 +57,26 @@
@Mock
private ProximitySensor mProximitySensor;
private SysuiStatusBarStateController mStatusBarStateController;
+ private FalsingDataProvider mFalsingDataProvider;
+ private FakeBatteryController mFakeBatteryController;
private BrightLineFalsingManager mFalsingManager;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+ mFakeBatteryController = new FakeBatteryController(getLeakCheck());
DisplayMetrics dm = new DisplayMetrics();
dm.xdpi = 100;
dm.ydpi = 100;
dm.widthPixels = 100;
dm.heightPixels = 100;
- FalsingDataProvider falsingDataProvider = new FalsingDataProvider(dm);
+ mFalsingDataProvider = new FalsingDataProvider(dm, mFakeBatteryController);
DeviceConfigProxy deviceConfigProxy = new DeviceConfigProxyFake();
DockManager dockManager = new DockManagerFake();
mStatusBarStateController = new StatusBarStateControllerImpl(new UiEventLoggerFake());
mStatusBarStateController.setState(StatusBarState.KEYGUARD);
- mFalsingManager = new BrightLineFalsingManager(falsingDataProvider,
+ mFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider,
mKeyguardUpdateMonitor, mProximitySensor, deviceConfigProxy, dockManager,
mStatusBarStateController);
}
@@ -83,6 +88,13 @@
}
@Test
+ public void testNoProximityWhenWirelessCharging() {
+ mFakeBatteryController.setWirelessCharging(true);
+ mFalsingManager.onScreenTurningOn();
+ verify(mProximitySensor, never()).register(any(ThresholdSensor.Listener.class));
+ }
+
+ @Test
public void testUnregisterSensor() {
mFalsingManager.onScreenTurningOn();
reset(mProximitySensor);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java
index 3ba5d1a..a4d198a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java
@@ -21,29 +21,30 @@
import android.util.DisplayMetrics;
import android.view.MotionEvent;
-import com.android.systemui.SysuiTestCase;
+import com.android.systemui.utils.leaks.FakeBatteryController;
+import com.android.systemui.utils.leaks.LeakCheckedTest;
import org.junit.After;
-import org.junit.Before;
import java.util.ArrayList;
import java.util.List;
-public class ClassifierTest extends SysuiTestCase {
+public class ClassifierTest extends LeakCheckedTest {
private FalsingDataProvider mDataProvider;
private List<MotionEvent> mMotionEvents = new ArrayList<>();
private float mOffsetX = 0;
private float mOffsetY = 0;
+ private FakeBatteryController mFakeBatteryController;
- @Before
public void setup() {
DisplayMetrics displayMetrics = new DisplayMetrics();
displayMetrics.xdpi = 100;
displayMetrics.ydpi = 100;
displayMetrics.widthPixels = 1000;
displayMetrics.heightPixels = 1000;
- mDataProvider = new FalsingDataProvider(displayMetrics);
+ mFakeBatteryController = new FakeBatteryController(getLeakCheck());
+ mDataProvider = new FalsingDataProvider(displayMetrics, mFakeBatteryController);
mDataProvider.setInteractionType(UNLOCK);
}
@@ -56,6 +57,10 @@
return mDataProvider;
}
+ FakeBatteryController getFakeBatteryController() {
+ return mFakeBatteryController;
+ }
+
void setOffsetX(float offsetX) {
mOffsetX = offsetX;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java
index 448c2f7..f13bc73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java
@@ -26,6 +26,8 @@
import androidx.test.filters.SmallTest;
+import com.android.systemui.utils.leaks.FakeBatteryController;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -37,17 +39,19 @@
@RunWith(AndroidTestingRunner.class)
public class FalsingDataProviderTest extends ClassifierTest {
+ private FakeBatteryController mFakeBatteryController;
private FalsingDataProvider mDataProvider;
@Before
public void setup() {
super.setup();
+ mFakeBatteryController = new FakeBatteryController(getLeakCheck());
DisplayMetrics displayMetrics = new DisplayMetrics();
displayMetrics.xdpi = 100;
displayMetrics.ydpi = 100;
displayMetrics.widthPixels = 1000;
displayMetrics.heightPixels = 1000;
- mDataProvider = new FalsingDataProvider(displayMetrics);
+ mDataProvider = new FalsingDataProvider(displayMetrics, mFakeBatteryController);
}
@After
@@ -246,4 +250,12 @@
assertThat(mDataProvider.isUp(), is(false));
mDataProvider.onSessionEnd();
}
+
+ @Test
+ public void test_isWirelessCharging() {
+ assertThat(mDataProvider.isWirelessCharging(), is(false));
+
+ mFakeBatteryController.setWirelessCharging(true);
+ assertThat(mDataProvider.isWirelessCharging(), is(true));
+ }
}
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/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
index 3c6e19f..7bc15dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
@@ -72,7 +72,8 @@
@Mock private lateinit var lmmFactory: LocalMediaManagerFactory
@Mock private lateinit var lmm: LocalMediaManager
@Mock private lateinit var mr2: MediaRouter2Manager
- private lateinit var fakeExecutor: FakeExecutor
+ private lateinit var fakeFgExecutor: FakeExecutor
+ private lateinit var fakeBgExecutor: FakeExecutor
@Mock private lateinit var dumpster: DumpManager
@Mock private lateinit var listener: MediaDeviceManager.Listener
@Mock private lateinit var device: MediaDevice
@@ -87,9 +88,10 @@
@Before
fun setUp() {
- fakeExecutor = FakeExecutor(FakeSystemClock())
- manager = MediaDeviceManager(context, lmmFactory, mr2, fakeExecutor, mediaDataManager,
- dumpster)
+ fakeFgExecutor = FakeExecutor(FakeSystemClock())
+ fakeBgExecutor = FakeExecutor(FakeSystemClock())
+ manager = MediaDeviceManager(context, lmmFactory, mr2, fakeFgExecutor, fakeBgExecutor,
+ mediaDataManager, dumpster)
manager.addListener(listener)
// Configure mocks.
@@ -144,13 +146,15 @@
fun loadAndRemoveMediaData() {
manager.onMediaDataLoaded(KEY, null, mediaData)
manager.onMediaDataRemoved(KEY)
+ fakeBgExecutor.runAllReady()
verify(lmm).unregisterCallback(any())
}
@Test
fun loadMediaDataWithNullToken() {
manager.onMediaDataLoaded(KEY, null, mediaData.copy(token = null))
- fakeExecutor.runAllReady()
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
val data = captureDeviceData(KEY)
assertThat(data.enabled).isTrue()
assertThat(data.name).isEqualTo(DEVICE_NAME)
@@ -163,6 +167,8 @@
reset(listener)
// WHEN data is loaded with a new key
manager.onMediaDataLoaded(KEY, KEY_OLD, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
// THEN the listener for the old key should removed.
verify(lmm).unregisterCallback(any())
// AND a new device event emitted
@@ -186,6 +192,8 @@
fun unknownOldKey() {
val oldKey = "unknown"
manager.onMediaDataLoaded(KEY, oldKey, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
verify(listener).onMediaDeviceChanged(eq(KEY), eq(oldKey), any())
}
@@ -193,13 +201,16 @@
fun updateToSessionTokenWithNullRoute() {
// GIVEN that media data has been loaded with a null token
manager.onMediaDataLoaded(KEY, null, mediaData.copy(token = null))
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ reset(listener)
// WHEN media data is loaded with a different token
// AND that token results in a null route
- reset(listener)
whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
// THEN the device should be disabled
- fakeExecutor.runAllReady()
val data = captureDeviceData(KEY)
assertThat(data.enabled).isFalse()
assertThat(data.name).isNull()
@@ -210,7 +221,8 @@
fun deviceEventOnAddNotification() {
// WHEN a notification is added
manager.onMediaDataLoaded(KEY, null, mediaData)
- val deviceCallback = captureCallback()
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
// THEN the update is dispatched to the listener
val data = captureDeviceData(KEY)
assertThat(data.enabled).isTrue()
@@ -230,10 +242,12 @@
@Test
fun deviceListUpdate() {
manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
val deviceCallback = captureCallback()
// WHEN the device list changes
deviceCallback.onDeviceListUpdate(mutableListOf(device))
- assertThat(fakeExecutor.runAllReady()).isEqualTo(1)
+ assertThat(fakeBgExecutor.runAllReady()).isEqualTo(1)
+ assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1)
// THEN the update is dispatched to the listener
val data = captureDeviceData(KEY)
assertThat(data.enabled).isTrue()
@@ -244,10 +258,12 @@
@Test
fun selectedDeviceStateChanged() {
manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
val deviceCallback = captureCallback()
// WHEN the selected device changes state
deviceCallback.onSelectedDeviceStateChanged(device, 1)
- assertThat(fakeExecutor.runAllReady()).isEqualTo(1)
+ assertThat(fakeBgExecutor.runAllReady()).isEqualTo(1)
+ assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1)
// THEN the update is dispatched to the listener
val data = captureDeviceData(KEY)
assertThat(data.enabled).isTrue()
@@ -270,6 +286,8 @@
whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
// WHEN a notification is added
manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
// THEN the device is disabled
val data = captureDeviceData(KEY)
assertThat(data.enabled).isFalse()
@@ -281,13 +299,16 @@
fun deviceDisabledWhenMR2ReturnsNullRouteInfoOnDeviceChanged() {
// GIVEN a notif is added
manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
reset(listener)
// AND MR2Manager returns null for routing session
whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
// WHEN the selected device changes state
val deviceCallback = captureCallback()
deviceCallback.onSelectedDeviceStateChanged(device, 1)
- fakeExecutor.runAllReady()
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
// THEN the device is disabled
val data = captureDeviceData(KEY)
assertThat(data.enabled).isFalse()
@@ -299,13 +320,16 @@
fun deviceDisabledWhenMR2ReturnsNullRouteInfoOnDeviceListUpdate() {
// GIVEN a notif is added
manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
reset(listener)
// GIVEN that MR2Manager returns null for routing session
whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
// WHEN the selected device changes state
val deviceCallback = captureCallback()
deviceCallback.onDeviceListUpdate(mutableListOf(device))
- fakeExecutor.runAllReady()
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
// THEN the device is disabled
val data = captureDeviceData(KEY)
assertThat(data.enabled).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
index e9a0a40..7155460 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
@@ -69,7 +69,7 @@
fun seekBarGone() {
// WHEN seek bar is disabled
val isEnabled = false
- val data = SeekBarViewModel.Progress(isEnabled, false, null, null)
+ val data = SeekBarViewModel.Progress(isEnabled, false, null, 0)
observer.onChanged(data)
// THEN seek bar shows just a thin line with no text
assertThat(seekBarView.isEnabled()).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
index c8ef9fb..b81ab74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
@@ -204,6 +204,22 @@
}
@Test
+ fun updateDurationNoMetadata() {
+ // GIVEN that the metadata is null
+ whenever(mockController.getMetadata()).thenReturn(null)
+ // AND a valid playback state (ie. media session is not destroyed)
+ val state = PlaybackState.Builder().run {
+ setState(PlaybackState.STATE_PLAYING, 200L, 1f)
+ build()
+ }
+ whenever(mockController.getPlaybackState()).thenReturn(state)
+ // WHEN the controller is updated
+ viewModel.updateController(mockController)
+ // THEN the seek bar is disabled
+ assertThat(viewModel.progress.value!!.enabled).isFalse()
+ }
+
+ @Test
fun updateElapsedTime() {
// GIVEN that the PlaybackState contains the current position
val position = 200L
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
index 84a261b6..3231b28 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -41,7 +41,7 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayController;
import org.junit.Before;
import org.junit.Ignore;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java
index 180c450..3b284b14 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java
@@ -33,7 +33,7 @@
import com.android.systemui.model.SysUiState;
import com.android.systemui.statusbar.phone.NavigationModeController;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayController;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
index b6b2217..55bec54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
@@ -33,7 +33,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.model.SysUiState;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayController;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
index 80fe0f0..3a4ba6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
@@ -31,7 +31,7 @@
import com.android.systemui.model.SysUiState;
import com.android.systemui.statusbar.phone.NavigationModeController;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayController;
import org.junit.Before;
import org.junit.Test;
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/pip/PipBoundsHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
index 70c2bba..e9d2b73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
@@ -33,7 +33,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.wm.DisplayController;
+import com.android.wm.shell.common.DisplayController;
import org.junit.Before;
import org.junit.Test;
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/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 5d4ef55..bdb7166 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -41,14 +41,17 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.CollectionUtils;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -331,7 +334,15 @@
private class TestTile extends QSTileImpl<QSTile.State> {
protected TestTile(QSHost host) {
- super(host);
+ super(
+ host,
+ mLooper.getLooper(),
+ new Handler(mLooper.getLooper()),
+ mock(MetricsLogger.class),
+ mock(StatusBarStateController.class),
+ mock(ActivityStarter.class),
+ mQSLogger
+ );
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index 953198c..c2579dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -23,17 +23,23 @@
import android.content.pm.ServiceInfo
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
+import android.os.Handler
import android.service.quicksettings.IQSTileService
import android.service.quicksettings.Tile
import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
import android.view.IWindowManager
-import androidx.test.runner.AndroidJUnit4
+import com.android.internal.logging.MetricsLogger
import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
-import junit.framework.Assert.assertFalse
-import junit.framework.Assert.assertTrue
+import com.android.systemui.qs.logging.QSLogger
import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -46,7 +52,8 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidJUnit4::class)
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
class CustomTileTest : SysuiTestCase() {
companion object {
@@ -56,36 +63,53 @@
val TILE_SPEC = CustomTile.toSpec(componentName)
}
- @Mock private lateinit var mTileHost: QSHost
- @Mock private lateinit var mTileService: IQSTileService
- @Mock private lateinit var mTileServices: TileServices
- @Mock private lateinit var mTileServiceManager: TileServiceManager
- @Mock private lateinit var mWindowService: IWindowManager
- @Mock private lateinit var mPackageManager: PackageManager
- @Mock private lateinit var mApplicationInfo: ApplicationInfo
- @Mock private lateinit var mServiceInfo: ServiceInfo
+ @Mock private lateinit var tileHost: QSHost
+ @Mock private lateinit var metricsLogger: MetricsLogger
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var qsLogger: QSLogger
+ @Mock private lateinit var tileService: IQSTileService
+ @Mock private lateinit var tileServices: TileServices
+ @Mock private lateinit var tileServiceManager: TileServiceManager
+ @Mock private lateinit var windowService: IWindowManager
+ @Mock private lateinit var packageManager: PackageManager
+ @Mock private lateinit var applicationInfo: ApplicationInfo
+ @Mock private lateinit var serviceInfo: ServiceInfo
private lateinit var customTile: CustomTile
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var customTileBuilder: CustomTile.Builder
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
- mContext.addMockSystemService("window", mWindowService)
- mContext.setMockPackageManager(mPackageManager)
- `when`(mTileHost.tileServices).thenReturn(mTileServices)
- `when`(mTileHost.context).thenReturn(mContext)
- `when`(mTileServices.getTileWrapper(any(CustomTile::class.java)))
- .thenReturn(mTileServiceManager)
- `when`(mTileServiceManager.tileService).thenReturn(mTileService)
- `when`(mPackageManager.getApplicationInfo(anyString(), anyInt()))
- .thenReturn(mApplicationInfo)
+ mContext.addMockSystemService("window", windowService)
+ mContext.setMockPackageManager(packageManager)
+ `when`(tileHost.tileServices).thenReturn(tileServices)
+ `when`(tileHost.context).thenReturn(mContext)
+ `when`(tileServices.getTileWrapper(any(CustomTile::class.java)))
+ .thenReturn(tileServiceManager)
+ `when`(tileServiceManager.tileService).thenReturn(tileService)
+ `when`(packageManager.getApplicationInfo(anyString(), anyInt()))
+ .thenReturn(applicationInfo)
- `when`(mPackageManager.getServiceInfo(any(ComponentName::class.java), anyInt()))
- .thenReturn(mServiceInfo)
- mServiceInfo.applicationInfo = mApplicationInfo
+ `when`(packageManager.getServiceInfo(any(ComponentName::class.java), anyInt()))
+ .thenReturn(serviceInfo)
+ serviceInfo.applicationInfo = applicationInfo
- customTile = CustomTile.create(mTileHost, TILE_SPEC, mContext)
+ customTileBuilder = CustomTile.Builder(
+ { tileHost },
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger
+ )
+
+ customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
}
@Test
@@ -93,18 +117,18 @@
assertEquals(0, customTile.user)
val userContext = mock(Context::class.java)
- `when`(userContext.packageManager).thenReturn(mPackageManager)
+ `when`(userContext.packageManager).thenReturn(packageManager)
`when`(userContext.userId).thenReturn(10)
- val tile = CustomTile.create(mTileHost, TILE_SPEC, userContext)
+ val tile = CustomTile.create(customTileBuilder, TILE_SPEC, userContext)
assertEquals(10, tile.user)
}
@Test
fun testToggleableTileHasBooleanState() {
- `when`(mTileServiceManager.isToggleableTile).thenReturn(true)
- customTile = CustomTile.create(mTileHost, TILE_SPEC, mContext)
+ `when`(tileServiceManager.isToggleableTile).thenReturn(true)
+ customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
assertTrue(customTile.state is QSTile.BooleanState)
assertTrue(customTile.newTileState() is QSTile.BooleanState)
@@ -118,8 +142,8 @@
@Test
fun testValueUpdatedInBooleanTile() {
- `when`(mTileServiceManager.isToggleableTile).thenReturn(true)
- customTile = CustomTile.create(mTileHost, TILE_SPEC, mContext)
+ `when`(tileServiceManager.isToggleableTile).thenReturn(true)
+ customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
customTile.qsTile.icon = mock(Icon::class.java)
`when`(customTile.qsTile.icon.loadDrawable(any(Context::class.java)))
.thenReturn(mock(Drawable::class.java))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 20f13bb..103e558 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -35,17 +35,14 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.argThat;
-import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static java.lang.Thread.sleep;
-
import android.content.Intent;
import android.metrics.LogMaker;
+import android.os.Handler;
+import android.os.Looper;
import android.service.quicksettings.Tile;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -57,7 +54,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile;
@@ -69,7 +65,6 @@
import com.android.systemui.statusbar.StatusBarState;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -91,9 +86,14 @@
private TestableLooper mTestableLooper;
private TileImpl mTile;
+ @Mock
private QSTileHost mHost;
+ @Mock
private MetricsLogger mMetricsLogger;
+ @Mock
private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private ActivityStarter mActivityStarter;
private UiEventLoggerFake mUiEventLoggerFake;
private InstanceId mInstanceId = InstanceId.fakeInstanceId(5);
@@ -104,21 +104,16 @@
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
mTestableLooper = TestableLooper.get(this);
- mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
- mDependency.injectMockDependency(ActivityStarter.class);
- mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
mUiEventLoggerFake = new UiEventLoggerFake();
- mStatusBarStateController =
- mDependency.injectMockDependency(StatusBarStateController.class);
- mHost = mock(QSTileHost.class);
when(mHost.indexOf(SPEC)).thenReturn(POSITION);
when(mHost.getContext()).thenReturn(mContext.getBaseContext());
- when(mHost.getQSLogger()).thenReturn(mQsLogger);
when(mHost.getUiEventLogger()).thenReturn(mUiEventLoggerFake);
when(mHost.getNewInstanceId()).thenReturn(mInstanceId);
- mTile = spy(new TileImpl(mHost));
- mTile.mHandler = mTile.new H(mTestableLooper.getLooper());
+ Handler mainHandler = new Handler(mTestableLooper.getLooper());
+
+ mTile = new TileImpl(mHost, mTestableLooper.getLooper(), mainHandler,
+ mMetricsLogger, mStatusBarStateController, mActivityStarter, mQsLogger);
mTile.setTileSpec(SPEC);
}
@@ -223,40 +218,25 @@
verify(maker).addTaggedData(eq(FIELD_QS_POSITION), eq(POSITION));
}
- @Test
- @Ignore("flaky")
- public void testStaleTimeout() throws InterruptedException {
- when(mTile.getStaleTimeout()).thenReturn(5l);
- clearInvocations(mTile);
-
- mTile.handleRefreshState(null);
- mTestableLooper.processAllMessages();
- verify(mTile, never()).handleStale();
-
- sleep(10);
- mTestableLooper.processAllMessages();
- verify(mTile).handleStale();
- }
+ //TODO(b/161799397) Bring back testStaleTimeout when we can use FakeExecutor
@Test
public void testStaleListening() {
mTile.handleStale();
mTestableLooper.processAllMessages();
- verify(mTile).handleSetListening(eq(true));
+ verify(mQsLogger).logTileChangeListening(SPEC, true);
mTile.handleRefreshState(null);
mTestableLooper.processAllMessages();
- verify(mTile).handleSetListening(eq(false));
+ verify(mQsLogger).logTileChangeListening(SPEC, false);
}
@Test
public void testHandleDestroyClearsHandlerQueue() {
- when(mTile.getStaleTimeout()).thenReturn(0L);
mTile.handleRefreshState(null); // this will add a delayed H.STALE message
mTile.handleDestroy();
- mTestableLooper.processAllMessages();
- verify(mTile, never()).handleStale();
+ assertFalse(mTile.mHandler.hasMessages(QSTileImpl.H.STALE));
}
@Test
@@ -359,8 +339,17 @@
}
private static class TileImpl extends QSTileImpl<QSTile.BooleanState> {
- protected TileImpl(QSHost host) {
- super(host);
+ protected TileImpl(
+ QSHost host,
+ Looper backgroundLooper,
+ Handler mainHandler,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger
+ ) {
+ super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+ activityStarter, qsLogger);
getState().state = Tile.STATE_ACTIVE;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
index 3199287..f70106a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
@@ -17,13 +17,17 @@
package com.android.systemui.qs.tiles
import android.content.Context
+import android.os.Handler
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
-import com.android.systemui.Dependency
+import com.android.internal.logging.MetricsLogger
import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.statusbar.policy.BatteryController
import org.junit.Assert.assertEquals
import org.junit.Before
@@ -47,6 +51,14 @@
@Mock
private lateinit var qsHost: QSHost
@Mock
+ private lateinit var metricsLogger: MetricsLogger
+ @Mock
+ private lateinit var statusBarStateController: StatusBarStateController
+ @Mock
+ private lateinit var activityStarter: ActivityStarter
+ @Mock
+ private lateinit var qsLogger: QSLogger
+ @Mock
private lateinit var batteryController: BatteryController
private lateinit var testableLooper: TestableLooper
private lateinit var tile: BatterySaverTile
@@ -55,11 +67,18 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
- mDependency.injectTestDependency(Dependency.BG_LOOPER, testableLooper.looper)
`when`(qsHost.userContext).thenReturn(userContext)
`when`(userContext.userId).thenReturn(USER)
- tile = BatterySaverTile(qsHost, batteryController)
+ tile = BatterySaverTile(
+ qsHost,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ batteryController)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 853b2db..8ece622 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -27,6 +27,7 @@
import android.media.MediaRouter;
import android.media.MediaRouter.RouteInfo;
import android.media.projection.MediaProjectionInfo;
+import android.os.Handler;
import android.service.quicksettings.Tile;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -34,10 +35,12 @@
import androidx.lifecycle.LifecycleOwner;
import androidx.test.filters.SmallTest;
-import com.android.systemui.Dependency;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -71,6 +74,12 @@
private QSTileHost mHost;
@Mock
NetworkController.SignalCallback mCallback;
+ @Mock
+ private MetricsLogger mMetricsLogger;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private QSLogger mQSLogger;
private TestableLooper mTestableLooper;
private CastTile mCastTile;
@@ -80,16 +89,20 @@
MockitoAnnotations.initMocks(this);
mTestableLooper = TestableLooper.get(this);
- mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
- mController = mDependency.injectMockDependency(CastController.class);
- mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class);
- mKeyguard = mDependency.injectMockDependency(KeyguardStateController.class);
- mNetworkController = mDependency.injectMockDependency(NetworkController.class);
-
when(mHost.getContext()).thenReturn(mContext);
- mCastTile = new CastTile(mHost, mController, mKeyguard, mNetworkController,
- mActivityStarter);
+ mCastTile = new CastTile(
+ mHost,
+ mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQSLogger,
+ mController,
+ mKeyguard,
+ mNetworkController
+ );
// We are not setting the mocks to listening, so we trigger a first refresh state to
// set the initial state
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index 5a68238..2d276bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -23,16 +23,20 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.os.Handler;
import android.service.quicksettings.Tile;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
-import com.android.systemui.Dependency;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
@@ -43,7 +47,7 @@
import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper()
@SmallTest
public class ScreenRecordTileTest extends SysuiTestCase {
@@ -53,6 +57,14 @@
private QSTileHost mHost;
@Mock
private KeyguardDismissUtil mKeyguardDismissUtil;
+ @Mock
+ private MetricsLogger mMetricsLogger;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private ActivityStarter mActivityStarter;
+ @Mock
+ private QSLogger mQSLogger;
private TestableLooper mTestableLooper;
private ScreenRecordTile mTile;
@@ -62,12 +74,20 @@
MockitoAnnotations.initMocks(this);
mTestableLooper = TestableLooper.get(this);
- mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
- mController = mDependency.injectMockDependency(RecordingController.class);
when(mHost.getContext()).thenReturn(mContext);
- mTile = new ScreenRecordTile(mHost, mController, mKeyguardDismissUtil);
+ mTile = new ScreenRecordTile(
+ mHost,
+ mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQSLogger,
+ mController,
+ mKeyguardDismissUtil
+ );
}
// Test that the tile is inactive and labeled correctly when the controller is neither starting
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index e98b6b6..4c9e141 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -32,7 +32,9 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import org.junit.Before;
import org.junit.Test;
@@ -61,6 +63,12 @@
private Executor mExecutor;
@Mock
private CurrentUserContextTracker mUserContextTracker;
+ private KeyguardDismissUtil mKeyguardDismissUtil = new KeyguardDismissUtil() {
+ public void executeWhenUnlocked(ActivityStarter.OnDismissAction action,
+ boolean requiresShadeOpen) {
+ action.onDismiss();
+ }
+ };
private RecordingService mRecordingService;
@@ -68,7 +76,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mRecordingService = Mockito.spy(new RecordingService(mController, mExecutor, mUiEventLogger,
- mNotificationManager, mUserContextTracker));
+ mNotificationManager, mUserContextTracker, mKeyguardDismissUtil));
// Return actual context info
doReturn(mContext).when(mRecordingService).getApplicationContext();
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/collection/coordinator/AppOpsCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
index 314b191..960ea79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
@@ -16,6 +16,11 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -43,6 +48,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -68,13 +74,13 @@
@Mock private AppOpsController mAppOpsController;
@Mock private NotifPipeline mNotifPipeline;
- private NotificationEntry mEntry;
- private Notification mNotification;
+ private NotificationEntryBuilder mEntryBuilder;
private AppOpsCoordinator mAppOpsCoordinator;
private NotifFilter mForegroundFilter;
private NotifCollectionListener mNotifCollectionListener;
private AppOpsController.Callback mAppOpsCallback;
private NotifLifetimeExtender mForegroundNotifLifetimeExtender;
+ private NotifSection mFgsSection;
private FakeSystemClock mClock = new FakeSystemClock();
private FakeExecutor mExecutor = new FakeExecutor(mClock);
@@ -90,11 +96,8 @@
mAppOpsController,
mExecutor);
- mNotification = new Notification();
- mEntry = new NotificationEntryBuilder()
- .setUser(new UserHandle(NOTIF_USER_ID))
- .setNotification(mNotification)
- .build();
+ mEntryBuilder = new NotificationEntryBuilder()
+ .setUser(new UserHandle(NOTIF_USER_ID));
mAppOpsCoordinator.attach(mNotifPipeline);
@@ -122,11 +125,14 @@
ArgumentCaptor.forClass(AppOpsController.Callback.class);
verify(mAppOpsController).addCallback(any(int[].class), appOpsCaptor.capture());
mAppOpsCallback = appOpsCaptor.getValue();
+
+ mFgsSection = mAppOpsCoordinator.getSection();
}
@Test
public void filterTest_disclosureUnnecessary() {
- StatusBarNotification sbn = mEntry.getSbn();
+ NotificationEntry entry = mEntryBuilder.build();
+ StatusBarNotification sbn = entry.getSbn();
// GIVEN the notification is a disclosure notification
when(mForegroundServiceController.isDisclosureNotification(sbn)).thenReturn(true);
@@ -136,84 +142,91 @@
.thenReturn(false);
// THEN filter out the notification
- assertTrue(mForegroundFilter.shouldFilterOut(mEntry, 0));
+ assertTrue(mForegroundFilter.shouldFilterOut(entry, 0));
}
@Test
public void filterTest_systemAlertNotificationUnnecessary() {
- StatusBarNotification sbn = mEntry.getSbn();
+ // GIVEN the alert notification isn't needed for this user
+ final Bundle extras = new Bundle();
+ extras.putStringArray(Notification.EXTRA_FOREGROUND_APPS,
+ new String[]{TEST_PKG});
+ mEntryBuilder.modifyNotification(mContext)
+ .setExtras(extras);
+ NotificationEntry entry = mEntryBuilder.build();
+ StatusBarNotification sbn = entry.getSbn();
+ when(mForegroundServiceController.isSystemAlertWarningNeeded(sbn.getUserId(), TEST_PKG))
+ .thenReturn(false);
// GIVEN the notification is a system alert notification + not a disclosure notification
when(mForegroundServiceController.isSystemAlertNotification(sbn)).thenReturn(true);
when(mForegroundServiceController.isDisclosureNotification(sbn)).thenReturn(false);
- // GIVEN the alert notification isn't needed for this user
- final Bundle extras = new Bundle();
- extras.putStringArray(Notification.EXTRA_FOREGROUND_APPS,
- new String[]{TEST_PKG});
- mNotification.extras = extras;
- when(mForegroundServiceController.isSystemAlertWarningNeeded(sbn.getUserId(), TEST_PKG))
- .thenReturn(false);
// THEN filter out the notification
- assertTrue(mForegroundFilter.shouldFilterOut(mEntry, 0));
+ assertTrue(mForegroundFilter.shouldFilterOut(entry, 0));
}
@Test
public void filterTest_doNotFilter() {
- StatusBarNotification sbn = mEntry.getSbn();
+ NotificationEntry entry = mEntryBuilder.build();
+ StatusBarNotification sbn = entry.getSbn();
// GIVEN the notification isn't a system alert notification nor a disclosure notification
when(mForegroundServiceController.isSystemAlertNotification(sbn)).thenReturn(false);
when(mForegroundServiceController.isDisclosureNotification(sbn)).thenReturn(false);
// THEN don't filter out the notification
- assertFalse(mForegroundFilter.shouldFilterOut(mEntry, 0));
+ assertFalse(mForegroundFilter.shouldFilterOut(entry, 0));
}
@Test
public void extendLifetimeText_notForeground() {
// GIVEN the notification doesn't represent a foreground service
- mNotification.flags = 0;
+ mEntryBuilder.modifyNotification(mContext)
+ .setFlag(FLAG_FOREGROUND_SERVICE, false);
// THEN don't extend the lifetime
assertFalse(mForegroundNotifLifetimeExtender
- .shouldExtendLifetime(mEntry, NotificationListenerService.REASON_CLICK));
+ .shouldExtendLifetime(mEntryBuilder.build(),
+ NotificationListenerService.REASON_CLICK));
}
@Test
public void extendLifetimeText_foregroundNotifRecentlyPosted() {
// GIVEN the notification represents a foreground service that was just posted
- mNotification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
- mEntry = new NotificationEntryBuilder()
- .setUser(new UserHandle(NOTIF_USER_ID))
+ Notification notification = new Notification.Builder(mContext, "test_channel")
+ .setFlag(FLAG_FOREGROUND_SERVICE, true)
+ .build();
+ NotificationEntry entry = mEntryBuilder
.setSbn(new StatusBarNotification(TEST_PKG, TEST_PKG, NOTIF_USER_ID, "",
- NOTIF_USER_ID, NOTIF_USER_ID, mNotification,
+ NOTIF_USER_ID, NOTIF_USER_ID, notification,
new UserHandle(NOTIF_USER_ID), "", System.currentTimeMillis()))
- .setNotification(mNotification)
+ .setNotification(notification)
.build();
// THEN extend the lifetime
assertTrue(mForegroundNotifLifetimeExtender
- .shouldExtendLifetime(mEntry, NotificationListenerService.REASON_CLICK));
+ .shouldExtendLifetime(entry, NotificationListenerService.REASON_CLICK));
}
@Test
public void extendLifetimeText_foregroundNotifOld() {
// GIVEN the notification represents a foreground service that was posted 10 seconds ago
- mNotification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
- mEntry = new NotificationEntryBuilder()
- .setUser(new UserHandle(NOTIF_USER_ID))
+ Notification notification = new Notification.Builder(mContext, "test_channel")
+ .setFlag(FLAG_FOREGROUND_SERVICE, true)
+ .build();
+ NotificationEntry entry = mEntryBuilder
.setSbn(new StatusBarNotification(TEST_PKG, TEST_PKG, NOTIF_USER_ID, "",
- NOTIF_USER_ID, NOTIF_USER_ID, mNotification,
+ NOTIF_USER_ID, NOTIF_USER_ID, notification,
new UserHandle(NOTIF_USER_ID), "",
System.currentTimeMillis() - 10000))
- .setNotification(mNotification)
+ .setNotification(notification)
.build();
// THEN don't extend the lifetime because the extended time exceeds MIN_FGS_TIME_MS
assertFalse(mForegroundNotifLifetimeExtender
- .shouldExtendLifetime(mEntry, NotificationListenerService.REASON_CLICK));
+ .shouldExtendLifetime(entry, NotificationListenerService.REASON_CLICK));
}
@Test
@@ -345,4 +358,41 @@
// THEN the entry's active app ops is updated to empty
assertTrue(entry.mActiveAppOps.isEmpty());
}
+
+ @Test
+ public void testIncludeFGSInSection_importanceDefault() {
+ // GIVEN the notification represents a colorized foreground service with > min importance
+ mEntryBuilder
+ .setFlag(mContext, FLAG_FOREGROUND_SERVICE, true)
+ .setImportance(IMPORTANCE_DEFAULT)
+ .modifyNotification(mContext).setColorized(true);
+
+ // THEN the entry is in the fgs section
+ assertTrue(mFgsSection.isInSection(mEntryBuilder.build()));
+ }
+
+ @Test
+ public void testDiscludeFGSInSection_importanceMin() {
+ // GIVEN the notification represents a colorized foreground service with min importance
+ mEntryBuilder
+ .setFlag(mContext, FLAG_FOREGROUND_SERVICE, true)
+ .setImportance(IMPORTANCE_MIN)
+ .modifyNotification(mContext).setColorized(true);
+
+ // THEN the entry is NOT in the fgs section
+ assertFalse(mFgsSection.isInSection(mEntryBuilder.build()));
+ }
+
+ @Test
+ public void testDiscludeNonFGSInSection() {
+ // GIVEN the notification represents a colorized notification with high importance that
+ // is NOT a foreground service
+ mEntryBuilder
+ .setImportance(IMPORTANCE_HIGH)
+ .setFlag(mContext, FLAG_FOREGROUND_SERVICE, false)
+ .modifyNotification(mContext).setColorized(false);
+
+ // THEN the entry is NOT in the fgs section
+ assertFalse(mFgsSection.isInSection(mEntryBuilder.build()));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
index dfc627e..be5c8a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
@@ -25,6 +25,9 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -40,38 +43,52 @@
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class ConversationCoordinatorTest : SysuiTestCase() {
-
- private var coordinator: ConversationCoordinator = ConversationCoordinator()
-
// captured listeners and pluggables:
- private var promoter: NotifPromoter? = null
+ private lateinit var promoter: NotifPromoter
+ private lateinit var peopleSection: NotifSection
@Mock
- private val pipeline: NotifPipeline? = null
+ private lateinit var pipeline: NotifPipeline
@Mock
- private val channel: NotificationChannel? = null
- private var entry: NotificationEntry? = null
+ private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
+ @Mock
+ private lateinit var channel: NotificationChannel
+ private lateinit var entry: NotificationEntry
+
+ private lateinit var coordinator: ConversationCoordinator
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- whenever(channel!!.isImportantConversation).thenReturn(true)
+ coordinator = ConversationCoordinator(peopleNotificationIdentifier)
+ whenever(channel.isImportantConversation).thenReturn(true)
- coordinator.attach(pipeline!!)
+ coordinator.attach(pipeline)
// capture arguments:
val notifPromoterCaptor = ArgumentCaptor.forClass(NotifPromoter::class.java)
verify(pipeline).addPromoter(notifPromoterCaptor.capture())
promoter = notifPromoterCaptor.value
+ peopleSection = coordinator.getSection()
+
entry = NotificationEntryBuilder().setChannel(channel).build()
}
@Test
- fun testPromotesCurrentHUN() {
-
+ fun testPromotesImportantConversations() {
// only promote important conversations
- assertTrue(promoter!!.shouldPromoteToTopLevel(entry))
- assertFalse(promoter!!.shouldPromoteToTopLevel(NotificationEntryBuilder().build()))
+ assertTrue(promoter.shouldPromoteToTopLevel(entry))
+ assertFalse(promoter.shouldPromoteToTopLevel(NotificationEntryBuilder().build()))
+ }
+
+ @Test
+ fun testInPeopleSection() {
+ whenever(peopleNotificationIdentifier.getPeopleNotificationType(
+ entry.sbn, entry.ranking)).thenReturn(TYPE_PERSON)
+
+ // only put people notifications in this section
+ assertTrue(peopleSection.isInSection(entry))
+ assertFalse(peopleSection.isInSection(NotificationEntryBuilder().build()))
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
index 85acbe6..5f10f38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -36,6 +36,8 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import org.junit.Before;
import org.junit.Test;
@@ -50,6 +52,7 @@
public class RankingCoordinatorTest extends SysuiTestCase {
@Mock private StatusBarStateController mStatusBarStateController;
+ @Mock private HighPriorityProvider mHighPriorityProvider;
@Mock private NotifPipeline mNotifPipeline;
@Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor;
@@ -58,16 +61,23 @@
private NotifFilter mCapturedSuspendedFilter;
private NotifFilter mCapturedDozingFilter;
+ private NotifSection mAlertingSection;
+ private NotifSection mSilentSection;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- RankingCoordinator rankingCoordinator = new RankingCoordinator(mStatusBarStateController);
+ RankingCoordinator rankingCoordinator =
+ new RankingCoordinator(mStatusBarStateController, mHighPriorityProvider);
mEntry = new NotificationEntryBuilder().build();
rankingCoordinator.attach(mNotifPipeline);
verify(mNotifPipeline, times(2)).addPreGroupFilter(mNotifFilterCaptor.capture());
mCapturedSuspendedFilter = mNotifFilterCaptor.getAllValues().get(0);
mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(1);
+
+ mAlertingSection = rankingCoordinator.getAlertingSection();
+ mSilentSection = rankingCoordinator.getSilentSection();
}
@Test
@@ -130,6 +140,26 @@
assertTrue(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
}
+ @Test
+ public void testIncludeInSectionAlerting() {
+ // GIVEN the entry is high priority
+ when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(true);
+
+ // THEN entry is in the alerting section
+ assertTrue(mAlertingSection.isInSection(mEntry));
+ assertFalse(mSilentSection.isInSection(mEntry));
+ }
+
+ @Test
+ public void testIncludeInSectionSilent() {
+ // GIVEN the entry isn't high priority
+ when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
+
+ // THEN entry is in the silent section
+ assertFalse(mAlertingSection.isInSection(mEntry));
+ assertTrue(mSilentSection.isInSection(mEntry));
+ }
+
private RankingBuilder getRankingForUnfilteredNotif() {
return new RankingBuilder()
.setKey(mEntry.getKey())
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/phone/NavigationBarContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java
index 6433376c..b5060ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -190,13 +191,13 @@
kbd2.setDarkIntensity(0f);
// Update icon returns the drawable intensity to half
- doReturn(kbd1).when(button).getNewDrawable();
- button.updateIcon();
+ doReturn(kbd1).when(button).getNewDrawable(anyInt(), anyInt());
+ button.updateIcon(0, 0);
assertEquals(TEST_DARK_INTENSITY, kbd1.getDarkIntensity(), DARK_INTENSITY_ERR);
// Return old dark intensity on new drawable after update icon
- doReturn(kbd2).when(button).getNewDrawable();
- button.updateIcon();
+ doReturn(kbd2).when(button).getNewDrawable(anyInt(), anyInt());
+ button.updateIcon(0, 0);
assertEquals(TEST_DARK_INTENSITY, kbd2.getDarkIntensity(), DARK_INTENSITY_ERR);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarRotationContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarRotationContextTest.java
index d6b38ff..f21235c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarRotationContextTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarRotationContextTest.java
@@ -59,8 +59,8 @@
final View view = new View(mContext);
mRotationButton = mock(RotationButton.class);
- mRotationButtonController = spy(
- new RotationButtonController(mContext, RES_UNDEF, mRotationButton));
+ mRotationButtonController = spy(new RotationButtonController(mContext, 0, 0,
+ mRotationButton));
final KeyButtonDrawable kbd = mock(KeyButtonDrawable.class);
doReturn(view).when(mRotationButton).getCurrentView();
doReturn(true).when(mRotationButton).acceptRotationProposal();
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/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
index 8ec4cb8..50c1e73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
@@ -25,6 +25,8 @@
public class FakeBatteryController extends BaseLeakChecker<BatteryStateChangeCallback>
implements BatteryController {
+ private boolean mWirelessCharging;
+
public FakeBatteryController(LeakCheck test) {
super(test, "battery");
}
@@ -58,4 +60,13 @@
public boolean isAodPowerSave() {
return false;
}
+
+ @Override
+ public boolean isWirelessCharging() {
+ return mWirelessCharging;
+ }
+
+ public void setWirelessCharging(boolean wirelessCharging) {
+ mWirelessCharging = wirelessCharging;
+ }
}
diff --git a/services/Android.bp b/services/Android.bp
index 8c251e9..f0144ac 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -154,7 +154,14 @@
java_library {
name: "android_system_server_stubs_current",
+ defaults: ["android_stubs_dists_default"],
srcs: [":services-stubs.sources"],
installable: false,
static_libs: ["android_module_lib_stubs_current"],
+ sdk_version: "none",
+ system_modules: "none",
+ java_version: "1.8",
+ dist: {
+ dir: "apistubs/android/system-server",
+ },
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index c92571c..2cd4c69 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -56,6 +56,8 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManager;
import android.hardware.fingerprint.IFingerprintService;
@@ -194,6 +196,9 @@
private final SimpleStringSplitter mStringColonSplitter =
new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
+ private final Rect mTempRect = new Rect();
+ private final Rect mTempRect1 = new Rect();
+
private final PackageManager mPackageManager;
private final PowerManager mPowerManager;
@@ -251,6 +256,7 @@
//TODO: Remove this hack
private boolean mInitialized;
+ private Point mTempPoint = new Point();
private boolean mIsAccessibilityButtonShown;
private AccessibilityUserState getCurrentUserStateLocked() {
@@ -1086,6 +1092,18 @@
}
/**
+ * Gets a point within the accessibility focused node where we can send down
+ * and up events to perform a click.
+ *
+ * @param outPoint The click point to populate.
+ * @return Whether accessibility a click point was found and set.
+ */
+ // TODO: (multi-display) Make sure this works for multiple displays.
+ public boolean getAccessibilityFocusClickPointInScreen(Point outPoint) {
+ return getInteractionBridge().getAccessibilityFocusClickPointInScreenNotLocked(outPoint);
+ }
+
+ /**
* Perform an accessibility action on the view that currently has accessibility focus.
* Has no effect if no item has accessibility focus, if the item with accessibility
* focus does not expose the specified action, or if the action fails.
@@ -1099,6 +1117,32 @@
return getInteractionBridge().performActionOnAccessibilityFocusedItemNotLocked(action);
}
+ /**
+ * Returns true if accessibility focus is confined to the active window.
+ */
+ public boolean accessibilityFocusOnlyInActiveWindow() {
+ synchronized (mLock) {
+ return mA11yWindowManager.isTrackingWindowsLocked();
+ }
+ }
+
+ /**
+ * Gets the bounds of a window.
+ *
+ * @param outBounds The output to which to write the bounds.
+ */
+ boolean getWindowBounds(int windowId, Rect outBounds) {
+ IBinder token;
+ synchronized (mLock) {
+ token = getWindowToken(windowId, mCurrentUserId);
+ }
+ mWindowManagerService.getWindowFrame(token, outBounds);
+ if (!outBounds.isEmpty()) {
+ return true;
+ }
+ return false;
+ }
+
public int getActiveWindowId() {
return mA11yWindowManager.getActiveWindowId(mCurrentUserId);
}
@@ -1877,9 +1921,11 @@
for (int i = 0; !observingWindows && (i < boundServiceCount); i++) {
AccessibilityServiceConnection boundService = boundServices.get(i);
if (boundService.canRetrieveInteractiveWindowsLocked()) {
+ userState.setAccessibilityFocusOnlyInActiveWindow(false);
observingWindows = true;
}
}
+ userState.setAccessibilityFocusOnlyInActiveWindow(true);
// Gets all valid displays and start tracking windows of each display if there is at least
// one bound service that can retrieve window content.
@@ -2997,6 +3043,19 @@
}
/**
+ * Gets a point within the accessibility focused node where we can send down and up events
+ * to perform a click.
+ *
+ * @param outPoint The click point to populate.
+ * @return Whether accessibility a click point was found and set.
+ */
+ // TODO: (multi-display) Make sure this works for multiple displays.
+ boolean getAccessibilityFocusClickPointInScreen(Point outPoint) {
+ return getInteractionBridge()
+ .getAccessibilityFocusClickPointInScreenNotLocked(outPoint);
+ }
+
+ /**
* Perform an accessibility action on the view that currently has accessibility focus.
* Has no effect if no item has accessibility focus, if the item with accessibility
* focus does not expose the specified action, or if the action fails.
@@ -3014,6 +3073,43 @@
return focus.performAction(action.getId());
}
+ public boolean getAccessibilityFocusClickPointInScreenNotLocked(Point outPoint) {
+ AccessibilityNodeInfo focus = getAccessibilityFocusNotLocked();
+ if (focus == null) {
+ return false;
+ }
+
+ synchronized (mLock) {
+ Rect boundsInScreen = mTempRect;
+ focus.getBoundsInScreen(boundsInScreen);
+
+ // Apply magnification if needed.
+ MagnificationSpec spec = getCompatibleMagnificationSpecLocked(focus.getWindowId());
+ if (spec != null && !spec.isNop()) {
+ boundsInScreen.offset((int) -spec.offsetX, (int) -spec.offsetY);
+ boundsInScreen.scale(1 / spec.scale);
+ }
+
+ // Clip to the window bounds.
+ Rect windowBounds = mTempRect1;
+ getWindowBounds(focus.getWindowId(), windowBounds);
+ if (!boundsInScreen.intersect(windowBounds)) {
+ return false;
+ }
+
+ // Clip to the screen bounds.
+ Point screenSize = mTempPoint;
+ mDefaultDisplay.getRealSize(screenSize);
+ if (!boundsInScreen.intersect(0, 0, screenSize.x, screenSize.y)) {
+ return false;
+ }
+
+ outPoint.set(boundsInScreen.centerX(), boundsInScreen.centerY());
+ }
+
+ return true;
+ }
+
private AccessibilityNodeInfo getAccessibilityFocusNotLocked() {
final int focusedWindowId;
synchronized (mLock) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index e54b0f9..f865aa7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -105,6 +105,7 @@
private boolean mIsDisplayMagnificationEnabled;
private boolean mIsFilterKeyEventsEnabled;
private boolean mIsPerformGesturesEnabled;
+ private boolean mAccessibilityFocusOnlyInActiveWindow;
private boolean mIsTextHighContrastEnabled;
private boolean mIsTouchExplorationEnabled;
private boolean mServiceHandlesDoubleTap;
@@ -742,6 +743,13 @@
mIsPerformGesturesEnabled = enabled;
}
+ public boolean isAccessibilityFocusOnlyInActiveWindow() {
+ return mAccessibilityFocusOnlyInActiveWindow;
+ }
+
+ public void setAccessibilityFocusOnlyInActiveWindow(boolean enabled) {
+ mAccessibilityFocusOnlyInActiveWindow = enabled;
+ }
public ComponentName getServiceChangingSoftKeyboardModeLocked() {
return mServiceChangingSoftKeyboardMode;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
index 667364c9..070626be 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
@@ -21,8 +21,11 @@
import static com.android.server.accessibility.gestures.TouchState.MAX_POINTER_COUNT;
import android.content.Context;
+import android.graphics.Point;
import android.util.Slog;
import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -37,19 +40,27 @@
*/
class EventDispatcher {
private static final String LOG_TAG = "EventDispatcher";
+ private static final int CLICK_LOCATION_NONE = 0;
+ private static final int CLICK_LOCATION_ACCESSIBILITY_FOCUS = 1;
+ private static final int CLICK_LOCATION_LAST_TOUCH_EXPLORED = 2;
private final AccessibilityManagerService mAms;
private Context mContext;
// The receiver of motion events.
private EventStreamTransformation mReceiver;
- // Keep track of which pointers sent to the system are down.
- private int mInjectedPointersDown;
- // The time of the last injected down.
- private long mLastInjectedDownEventTime;
+ // The long pressing pointer id if coordinate remapping is needed for double tap and hold
+ private int mLongPressingPointerId = -1;
- // The last injected hover event.
- private MotionEvent mLastInjectedHoverEvent;
+ // The long pressing pointer X if coordinate remapping is needed for double tap and hold.
+ private int mLongPressingPointerDeltaX;
+
+ // The long pressing pointer Y if coordinate remapping is needed for double tap and hold.
+ private int mLongPressingPointerDeltaY;
+
+ // Temporary point to avoid instantiation.
+ private final Point mTempPoint = new Point();
+
private TouchState mState;
EventDispatcher(
@@ -98,8 +109,18 @@
if (action == MotionEvent.ACTION_DOWN) {
event.setDownTime(event.getEventTime());
} else {
- event.setDownTime(getLastInjectedDownEventTime());
+ event.setDownTime(mState.getLastInjectedDownEventTime());
}
+ // If the user is long pressing but the long pressing pointer
+ // was not exactly over the accessibility focused item we need
+ // to remap the location of that pointer so the user does not
+ // have to explicitly touch explore something to be able to
+ // long press it, or even worse to avoid the user long pressing
+ // on the wrong item since click and long press behave differently.
+ if (mLongPressingPointerId >= 0) {
+ event = offsetEvent(event, -mLongPressingPointerDeltaX, -mLongPressingPointerDeltaY);
+ }
+
if (DEBUG) {
Slog.d(
LOG_TAG,
@@ -116,7 +137,7 @@
} else {
Slog.e(LOG_TAG, "Error sending event: no receiver specified.");
}
- updateState(event);
+ mState.onInjectedMotionEvent(event);
if (event != prototype) {
event.recycle();
@@ -145,87 +166,15 @@
mState.onInjectedAccessibilityEvent(type);
}
- /**
- * Processes an injected {@link MotionEvent} event.
- *
- * @param event The event to process.
- */
- void updateState(MotionEvent event) {
- final int action = event.getActionMasked();
- final int pointerId = event.getPointerId(event.getActionIndex());
- final int pointerFlag = (1 << pointerId);
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_POINTER_DOWN:
- mInjectedPointersDown |= pointerFlag;
- mLastInjectedDownEventTime = event.getDownTime();
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_POINTER_UP:
- mInjectedPointersDown &= ~pointerFlag;
- if (mInjectedPointersDown == 0) {
- mLastInjectedDownEventTime = 0;
- }
- break;
- case MotionEvent.ACTION_HOVER_ENTER:
- case MotionEvent.ACTION_HOVER_MOVE:
- case MotionEvent.ACTION_HOVER_EXIT:
- if (mLastInjectedHoverEvent != null) {
- mLastInjectedHoverEvent.recycle();
- }
- mLastInjectedHoverEvent = MotionEvent.obtain(event);
- break;
- }
- if (DEBUG) {
- Slog.i(LOG_TAG, "Injected pointer:\n" + toString());
- }
- }
-
- /** Clears the internals state. */
- public void clear() {
- mInjectedPointersDown = 0;
- }
-
- /** @return The time of the last injected down event. */
- public long getLastInjectedDownEventTime() {
- return mLastInjectedDownEventTime;
- }
-
- /** @return The number of down pointers injected to the view hierarchy. */
- public int getInjectedPointerDownCount() {
- return Integer.bitCount(mInjectedPointersDown);
- }
-
- /** @return The bits of the injected pointers that are down. */
- public int getInjectedPointersDown() {
- return mInjectedPointersDown;
- }
-
- /**
- * Whether an injected pointer is down.
- *
- * @param pointerId The unique pointer id.
- * @return True if the pointer is down.
- */
- public boolean isInjectedPointerDown(int pointerId) {
- final int pointerFlag = (1 << pointerId);
- return (mInjectedPointersDown & pointerFlag) != 0;
- }
-
- /** @return The the last injected hover event. */
- public MotionEvent getLastInjectedHoverEvent() {
- return mLastInjectedHoverEvent;
- }
-
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("=========================");
builder.append("\nDown pointers #");
- builder.append(Integer.bitCount(mInjectedPointersDown));
+ builder.append(Integer.bitCount(mState.getInjectedPointersDown()));
builder.append(" [ ");
for (int i = 0; i < MAX_POINTER_COUNT; i++) {
- if ((mInjectedPointersDown & i) != 0) {
+ if (mState.isInjectedPointerDown(i)) {
builder.append(i);
builder.append(" ");
}
@@ -236,6 +185,48 @@
}
/**
+ * /** Offsets all pointers in the given event by adding the specified X and Y offsets.
+ *
+ * @param event The event to offset.
+ * @param offsetX The X offset.
+ * @param offsetY The Y offset.
+ * @return An event with the offset pointers or the original event if both offsets are zero.
+ */
+ private MotionEvent offsetEvent(MotionEvent event, int offsetX, int offsetY) {
+ if (offsetX == 0 && offsetY == 0) {
+ return event;
+ }
+ final int remappedIndex = event.findPointerIndex(mLongPressingPointerId);
+ final int pointerCount = event.getPointerCount();
+ PointerProperties[] props = PointerProperties.createArray(pointerCount);
+ PointerCoords[] coords = PointerCoords.createArray(pointerCount);
+ for (int i = 0; i < pointerCount; i++) {
+ event.getPointerProperties(i, props[i]);
+ event.getPointerCoords(i, coords[i]);
+ if (i == remappedIndex) {
+ coords[i].x += offsetX;
+ coords[i].y += offsetY;
+ }
+ }
+ return MotionEvent.obtain(
+ event.getDownTime(),
+ event.getEventTime(),
+ event.getAction(),
+ event.getPointerCount(),
+ props,
+ coords,
+ event.getMetaState(),
+ event.getButtonState(),
+ 1.0f,
+ 1.0f,
+ event.getDeviceId(),
+ event.getEdgeFlags(),
+ event.getSource(),
+ event.getDisplayId(),
+ event.getFlags());
+ }
+
+ /**
* Computes the action for an injected event based on a masked action and a pointer index.
*
* @param actionMasked The masked action.
@@ -247,7 +238,7 @@
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
// Compute the action based on how many down pointers are injected.
- if (getInjectedPointerDownCount() == 0) {
+ if (mState.getInjectedPointerDownCount() == 0) {
return MotionEvent.ACTION_DOWN;
} else {
return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
@@ -255,7 +246,7 @@
}
case MotionEvent.ACTION_POINTER_UP:
// Compute the action based on how many down pointers are injected.
- if (getInjectedPointerDownCount() == 1) {
+ if (mState.getInjectedPointerDownCount() == 1) {
return MotionEvent.ACTION_UP;
} else {
return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
@@ -280,7 +271,7 @@
for (int i = 0; i < pointerCount; i++) {
final int pointerId = prototype.getPointerId(i);
// Do not send event for already delivered pointers.
- if (!isInjectedPointerDown(pointerId)) {
+ if (!mState.isInjectedPointerDown(pointerId)) {
pointerIdBits |= (1 << pointerId);
final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i);
sendMotionEvent(
@@ -306,7 +297,7 @@
for (int i = 0; i < pointerCount; i++) {
final int pointerId = prototype.getPointerId(i);
// Skip non injected down pointers.
- if (!isInjectedPointerDown(pointerId)) {
+ if (!mState.isInjectedPointerDown(pointerId)) {
continue;
}
final int action = computeInjectionAction(MotionEvent.ACTION_POINTER_UP, i);
@@ -315,4 +306,103 @@
pointerIdBits &= ~(1 << pointerId);
}
}
+
+ public boolean longPressWithTouchEvents(MotionEvent event, int policyFlags) {
+ final int pointerIndex = event.getActionIndex();
+ final int pointerId = event.getPointerId(pointerIndex);
+ Point clickLocation = mTempPoint;
+ final int result = computeClickLocation(clickLocation);
+ if (result == CLICK_LOCATION_NONE) {
+ return false;
+ }
+ mLongPressingPointerId = pointerId;
+ mLongPressingPointerDeltaX = (int) event.getX(pointerIndex) - clickLocation.x;
+ mLongPressingPointerDeltaY = (int) event.getY(pointerIndex) - clickLocation.y;
+ sendDownForAllNotInjectedPointers(event, policyFlags);
+ return true;
+ }
+
+ void clear() {
+ mLongPressingPointerId = -1;
+ mLongPressingPointerDeltaX = 0;
+ mLongPressingPointerDeltaY = 0;
+ }
+
+ public void clickWithTouchEvents(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ final int pointerIndex = event.getActionIndex();
+ final int pointerId = event.getPointerId(pointerIndex);
+ Point clickLocation = mTempPoint;
+ final int result = computeClickLocation(clickLocation);
+ if (result == CLICK_LOCATION_NONE) {
+ Slog.e(LOG_TAG, "Unable to compute click location.");
+ // We can't send a click to no location, but the gesture was still
+ // consumed.
+ return;
+ }
+ // Do the click.
+ PointerProperties[] properties = new PointerProperties[1];
+ properties[0] = new PointerProperties();
+ event.getPointerProperties(pointerIndex, properties[0]);
+ PointerCoords[] coords = new PointerCoords[1];
+ coords[0] = new PointerCoords();
+ coords[0].x = clickLocation.x;
+ coords[0].y = clickLocation.y;
+ MotionEvent clickEvent =
+ MotionEvent.obtain(
+ event.getDownTime(),
+ event.getEventTime(),
+ MotionEvent.ACTION_DOWN,
+ 1,
+ properties,
+ coords,
+ 0,
+ 0,
+ 1.0f,
+ 1.0f,
+ event.getDeviceId(),
+ 0,
+ event.getSource(),
+ event.getDisplayId(),
+ event.getFlags());
+ final boolean targetAccessibilityFocus = (result == CLICK_LOCATION_ACCESSIBILITY_FOCUS);
+ sendActionDownAndUp(clickEvent, rawEvent, policyFlags, targetAccessibilityFocus);
+ clickEvent.recycle();
+ }
+
+ private int computeClickLocation(Point outLocation) {
+ if (mState.getLastInjectedHoverEventForClick() != null) {
+ final int lastExplorePointerIndex =
+ mState.getLastInjectedHoverEventForClick().getActionIndex();
+ outLocation.x =
+ (int) mState.getLastInjectedHoverEventForClick().getX(lastExplorePointerIndex);
+ outLocation.y =
+ (int) mState.getLastInjectedHoverEventForClick().getY(lastExplorePointerIndex);
+ if (!mAms.accessibilityFocusOnlyInActiveWindow()
+ || mState.getLastTouchedWindowId() == mAms.getActiveWindowId()) {
+ if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) {
+ return CLICK_LOCATION_ACCESSIBILITY_FOCUS;
+ } else {
+ return CLICK_LOCATION_LAST_TOUCH_EXPLORED;
+ }
+ }
+ }
+ if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) {
+ return CLICK_LOCATION_ACCESSIBILITY_FOCUS;
+ }
+ return CLICK_LOCATION_NONE;
+ }
+
+ private void sendActionDownAndUp(
+ MotionEvent prototype,
+ MotionEvent rawEvent,
+ int policyFlags,
+ boolean targetAccessibilityFocus) {
+ // Tap with the pointer that last explored.
+ final int pointerId = prototype.getPointerId(prototype.getActionIndex());
+ final int pointerIdBits = (1 << pointerId);
+ prototype.setTargetAccessibilityFocus(targetAccessibilityFocus);
+ sendMotionEvent(prototype, MotionEvent.ACTION_DOWN, rawEvent, pointerIdBits, policyFlags);
+ prototype.setTargetAccessibilityFocus(targetAccessibilityFocus);
+ sendMotionEvent(prototype, MotionEvent.ACTION_UP, rawEvent, pointerIdBits, policyFlags);
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
index 6d0f069..e9c70c6 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -104,6 +104,7 @@
mHandler = new Handler(context.getMainLooper());
mListener = listener;
mState = state;
+ mMultiFingerGesturesEnabled = false;
// Set up gestures.
// Start with double tap.
mGestures.add(new MultiTap(context, 2, GESTURE_DOUBLE_TAP, this));
@@ -247,7 +248,7 @@
* and hold is dispatched via onGestureCompleted. Otherwise, this method is called when the
* user has performed a double tap and then held down the second tap.
*/
- void onDoubleTapAndHold();
+ void onDoubleTapAndHold(MotionEvent event, MotionEvent rawEvent, int policyFlags);
/**
* When FLAG_SERVICE_HANDLES_DOUBLE_TAP is enabled, this method is not called; double-tap is
@@ -256,7 +257,7 @@
*
* @return true if the event is consumed, else false
*/
- boolean onDoubleTap();
+ boolean onDoubleTap(MotionEvent event, MotionEvent rawEvent, int policyFlags);
/**
* Called when the system has decided the event stream is a potential gesture.
@@ -322,7 +323,7 @@
new AccessibilityGestureEvent(gestureId, event.getDisplayId());
mListener.onGestureCompleted(gestureEvent);
} else {
- mListener.onDoubleTap();
+ mListener.onDoubleTap(event, rawEvent, policyFlags);
}
clear();
break;
@@ -332,7 +333,7 @@
new AccessibilityGestureEvent(gestureId, event.getDisplayId());
mListener.onGestureCompleted(gestureEvent);
} else {
- mListener.onDoubleTapAndHold();
+ mListener.onDoubleTapAndHold(event, rawEvent, policyFlags);
}
clear();
break;
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 7b5180d..696702f 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -39,7 +39,6 @@
import android.accessibilityservice.AccessibilityGestureEvent;
import android.annotation.NonNull;
import android.content.Context;
-import android.graphics.Point;
import android.graphics.Region;
import android.os.Handler;
import android.util.Slog;
@@ -133,8 +132,6 @@
// Handle to the accessibility manager service.
private final AccessibilityManagerService mAms;
- // Temporary point to avoid instantiation.
- private final Point mTempPoint = new Point();
// Context in which this explorer operates.
private final Context mContext;
@@ -301,6 +298,7 @@
if (eventType == TYPE_VIEW_HOVER_EXIT) {
sendsPendingA11yEventsIfNeeded();
}
+ mState.onReceivedAccessibilityEvent(event);
super.onAccessibilityEvent(event);
}
@@ -332,16 +330,15 @@
}
@Override
- public void onDoubleTapAndHold() {
- // Try to use the standard accessibility API to long click
- if (!mAms.performActionOnAccessibilityFocusedItem(
- AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK)) {
- Slog.e(LOG_TAG, "ACTION_LONG_CLICK failed.");
+ public void onDoubleTapAndHold(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mDispatcher.longPressWithTouchEvents(event, policyFlags)) {
+ sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
+ mState.startDelegating();
}
}
@Override
- public boolean onDoubleTap() {
+ public boolean onDoubleTap(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
mAms.onTouchInteractionEnd();
// Remove pending event deliveries.
mSendHoverEnterAndMoveDelayed.cancel();
@@ -357,7 +354,10 @@
// Try to use the standard accessibility API to click
if (!mAms.performActionOnAccessibilityFocusedItem(
AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK)) {
- Slog.e(LOG_TAG, "ACTION_CLICK failed.");
+ Slog.e(LOG_TAG, "ACTION_CLICK failed. Dispatching motion events to simulate click.");
+
+ mDispatcher.clickWithTouchEvents(event, rawEvent, policyFlags);
+ return true;
}
return true;
}
@@ -819,6 +819,7 @@
// Announce the end of a the touch interaction.
mAms.onTouchInteractionEnd();
+ mDispatcher.clear();
mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_INTERACTION_END);
} break;
@@ -851,7 +852,7 @@
* @param policyFlags The policy flags associated with the event.
*/
private void sendHoverExitAndTouchExplorationGestureEndIfNeeded(int policyFlags) {
- MotionEvent event = mDispatcher.getLastInjectedHoverEvent();
+ MotionEvent event = mState.getLastInjectedHoverEvent();
if (event != null && event.getActionMasked() != ACTION_HOVER_EXIT) {
final int pointerIdBits = event.getPointerIdBits();
if (!mSendTouchExplorationEndDelayed.isPending()) {
@@ -873,7 +874,7 @@
* @param policyFlags The policy flags associated with the event.
*/
private void sendTouchExplorationGestureStartAndHoverEnterIfNeeded(int policyFlags) {
- MotionEvent event = mDispatcher.getLastInjectedHoverEvent();
+ MotionEvent event = mState.getLastInjectedHoverEvent();
if (event != null && event.getActionMasked() == ACTION_HOVER_EXIT) {
final int pointerIdBits = event.getPointerIdBits();
mDispatcher.sendMotionEvent(
@@ -1198,7 +1199,6 @@
+ ", mDetermineUserIntentTimeout: " + mDetermineUserIntentTimeout
+ ", mDoubleTapSlop: " + mDoubleTapSlop
+ ", mDraggingPointerId: " + mDraggingPointerId
- + ", mTempPoint: " + mTempPoint
+ " }";
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
index d23dbbe..7a39bc2 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
@@ -75,6 +75,16 @@
private MotionEvent mLastReceivedEvent;
// The accompanying raw event without any transformations.
private MotionEvent mLastReceivedRawEvent;
+ // The id of the last touch explored window.
+ private int mLastTouchedWindowId;
+ // The last injected hover event.
+ private MotionEvent mLastInjectedHoverEvent;
+ // The last injected hover event used for performing clicks.
+ private MotionEvent mLastInjectedHoverEventForClick;
+ // The time of the last injected down.
+ private long mLastInjectedDownEventTime;
+ // Keep track of which pointers sent to the system are down.
+ private int mInjectedPointersDown;
public TouchState() {
mReceivedPointerTracker = new ReceivedPointerTracker();
@@ -88,7 +98,9 @@
mLastReceivedEvent.recycle();
mLastReceivedEvent = null;
}
+ mLastTouchedWindowId = -1;
mReceivedPointerTracker.clear();
+ mInjectedPointersDown = 0;
}
/**
@@ -107,6 +119,71 @@
mReceivedPointerTracker.onMotionEvent(rawEvent);
}
+ /**
+ * Processes an injected {@link MotionEvent} event.
+ *
+ * @param event The event to process.
+ */
+ void onInjectedMotionEvent(MotionEvent event) {
+ final int action = event.getActionMasked();
+ final int pointerId = event.getPointerId(event.getActionIndex());
+ final int pointerFlag = (1 << pointerId);
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_POINTER_DOWN:
+ mInjectedPointersDown |= pointerFlag;
+ mLastInjectedDownEventTime = event.getDownTime();
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_POINTER_UP:
+ mInjectedPointersDown &= ~pointerFlag;
+ if (mInjectedPointersDown == 0) {
+ mLastInjectedDownEventTime = 0;
+ }
+ break;
+ case MotionEvent.ACTION_HOVER_ENTER:
+ case MotionEvent.ACTION_HOVER_MOVE:
+ if (mLastInjectedHoverEvent != null) {
+ mLastInjectedHoverEvent.recycle();
+ }
+ mLastInjectedHoverEvent = MotionEvent.obtain(event);
+ break;
+ case MotionEvent.ACTION_HOVER_EXIT:
+ if (mLastInjectedHoverEvent != null) {
+ mLastInjectedHoverEvent.recycle();
+ }
+ mLastInjectedHoverEvent = MotionEvent.obtain(event);
+ if (mLastInjectedHoverEventForClick != null) {
+ mLastInjectedHoverEventForClick.recycle();
+ }
+ mLastInjectedHoverEventForClick = MotionEvent.obtain(event);
+ break;
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Injected pointer:\n" + toString());
+ }
+ }
+
+ /** Updates state in response to an accessibility event received from the outside. */
+ public void onReceivedAccessibilityEvent(AccessibilityEvent event) {
+ // If a new window opens or the accessibility focus moves we no longer
+ // want to click/long press on the last touch explored location.
+ switch (event.getEventType()) {
+ case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
+ case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED:
+ if (mLastInjectedHoverEventForClick != null) {
+ mLastInjectedHoverEventForClick.recycle();
+ mLastInjectedHoverEventForClick = null;
+ }
+ mLastTouchedWindowId = -1;
+ break;
+ case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
+ case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT:
+ mLastTouchedWindowId = event.getWindowId();
+ break;
+ }
+ }
+
public void onInjectedAccessibilityEvent(int type) {
// The below state transitions go here because the related events are often sent on a
// delay.
@@ -236,6 +313,46 @@
return mLastReceivedEvent;
}
+ /** @return The the last injected hover event. */
+ public MotionEvent getLastInjectedHoverEvent() {
+ return mLastInjectedHoverEvent;
+ }
+
+ /** @return The time of the last injected down event. */
+ public long getLastInjectedDownEventTime() {
+ return mLastInjectedDownEventTime;
+ }
+
+ public int getLastTouchedWindowId() {
+ return mLastTouchedWindowId;
+ }
+
+ /** @return The number of down pointers injected to the view hierarchy. */
+ public int getInjectedPointerDownCount() {
+ return Integer.bitCount(mInjectedPointersDown);
+ }
+
+ /** @return The bits of the injected pointers that are down. */
+ public int getInjectedPointersDown() {
+ return mInjectedPointersDown;
+ }
+
+ /**
+ * Whether an injected pointer is down.
+ *
+ * @param pointerId The unique pointer id.
+ * @return True if the pointer is down.
+ */
+ public boolean isInjectedPointerDown(int pointerId) {
+ final int pointerFlag = (1 << pointerId);
+ return (mInjectedPointersDown & pointerFlag) != 0;
+ }
+
+ /** @return The the last injected hover event used for a click. */
+ public MotionEvent getLastInjectedHoverEventForClick() {
+ return mLastInjectedHoverEventForClick;
+ }
+
/** This class tracks where and when a pointer went down. It does not track its movement. */
class ReceivedPointerTracker {
private static final String LOG_TAG_RECEIVED_POINTER_TRACKER = "ReceivedPointerTracker";
diff --git a/services/backup/java/com/android/server/backup/DataChangedJournal.java b/services/backup/java/com/android/server/backup/DataChangedJournal.java
index e75eb73..0e7fc93 100644
--- a/services/backup/java/com/android/server/backup/DataChangedJournal.java
+++ b/services/backup/java/com/android/server/backup/DataChangedJournal.java
@@ -16,17 +16,21 @@
package com.android.server.backup;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Slog;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
+import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.function.Consumer;
/**
@@ -36,7 +40,7 @@
* <p>This information is persisted to the filesystem so that it is not lost in the event of a
* reboot.
*/
-public class DataChangedJournal {
+public final class DataChangedJournal {
private static final String TAG = "DataChangedJournal";
private static final String FILE_NAME_PREFIX = "journal";
@@ -50,10 +54,11 @@
/**
* Constructs an instance that reads from and writes to the given file.
*/
- DataChangedJournal(File file) {
- mFile = file;
+ DataChangedJournal(@NonNull File file) {
+ mFile = Objects.requireNonNull(file);
}
+
/**
* Adds the given package to the journal.
*
@@ -75,15 +80,17 @@
*/
public void forEach(Consumer<String> consumer) throws IOException {
try (
- BufferedInputStream bufferedInputStream = new BufferedInputStream(
- new FileInputStream(mFile), BUFFER_SIZE_BYTES);
- DataInputStream dataInputStream = new DataInputStream(bufferedInputStream)
+ InputStream in = new FileInputStream(mFile);
+ InputStream bufferedIn = new BufferedInputStream(in, BUFFER_SIZE_BYTES);
+ DataInputStream dataInputStream = new DataInputStream(bufferedIn)
) {
- while (dataInputStream.available() > 0) {
+ while (true) {
String packageName = dataInputStream.readUTF();
consumer.accept(packageName);
}
- }
+ } catch (EOFException tolerated) {
+ // no more data; we're done
+ } // other kinds of IOExceptions are error conditions and handled in the caller
}
/**
@@ -107,14 +114,15 @@
}
@Override
+ public int hashCode() {
+ return mFile.hashCode();
+ }
+
+ @Override
public boolean equals(@Nullable Object object) {
if (object instanceof DataChangedJournal) {
DataChangedJournal that = (DataChangedJournal) object;
- try {
- return this.mFile.getCanonicalPath().equals(that.mFile.getCanonicalPath());
- } catch (IOException exception) {
- return false;
- }
+ return mFile.equals(that.mFile);
}
return false;
}
@@ -131,9 +139,10 @@
* @return The journal.
* @throws IOException if there is an IO error creating the file.
*/
- static DataChangedJournal newJournal(File journalDirectory) throws IOException {
- return new DataChangedJournal(
- File.createTempFile(FILE_NAME_PREFIX, null, journalDirectory));
+ static DataChangedJournal newJournal(@NonNull File journalDirectory) throws IOException {
+ Objects.requireNonNull(journalDirectory);
+ File file = File.createTempFile(FILE_NAME_PREFIX, null, journalDirectory);
+ return new DataChangedJournal(file);
}
/**
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index ff21a73..2de26b7 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -156,6 +156,7 @@
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
@@ -1154,24 +1155,31 @@
private void parseLeftoverJournals() {
ArrayList<DataChangedJournal> journals = DataChangedJournal.listJournals(mJournalDir);
+ journals.removeAll(Collections.singletonList(mJournal));
+ if (!journals.isEmpty()) {
+ Slog.i(TAG, addUserIdToLogMessage(mUserId,
+ "Found " + journals.size() + " stale backup journal(s), scheduling."));
+ }
+ Set<String> packageNames = new LinkedHashSet<>();
for (DataChangedJournal journal : journals) {
- if (!journal.equals(mJournal)) {
- try {
- journal.forEach(packageName -> {
- Slog.i(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Found stale backup journal, scheduling"));
- if (MORE_DEBUG) {
- Slog.i(TAG, addUserIdToLogMessage(mUserId, " " + packageName));
- }
+ try {
+ journal.forEach(packageName -> {
+ if (packageNames.add(packageName)) {
dataChangedImpl(packageName);
- });
- } catch (IOException e) {
- Slog.e(TAG, addUserIdToLogMessage(mUserId, "Can't read " + journal), e);
- }
+ }
+ });
+ } catch (IOException e) {
+ Slog.e(TAG, addUserIdToLogMessage(mUserId, "Can't read " + journal), e);
}
}
+ if (!packageNames.isEmpty()) {
+ String msg = "Stale backup journals: Scheduled " + packageNames.size()
+ + " package(s) total";
+ if (MORE_DEBUG) {
+ msg += ": " + packageNames;
+ }
+ Slog.i(TAG, addUserIdToLogMessage(mUserId, msg));
+ }
}
public Set<String> getExcludedRestoreKeys(String packageName) {
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/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index dd485fe..1cb7c4d 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -25,21 +25,6 @@
{
"name": "CtsScopedStorageHostTest",
"file_patterns": ["StorageManagerService\\.java"]
- },
- {
- "name": "FrameworksMockingServicesTests",
- "file_patterns": ["AppStateTracker\\.java"],
- "options": [
- {
- "include-filter": "com.android.server.AppStateTrackerTest"
- },
- {
- "include-annotation": "android.platform.test.annotations.Presubmit"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
}
]
}
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/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 49676de..1815dac 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -23,7 +23,6 @@
import android.content.IntentFilter;
import android.hidl.manager.V1_0.IServiceManager;
import android.os.Binder;
-import android.os.Build;
import android.os.Debug;
import android.os.Handler;
import android.os.IPowerManager;
@@ -32,10 +31,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
-import android.system.StructRlimit;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -51,13 +46,8 @@
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -143,7 +133,6 @@
private IActivityController mController;
private boolean mAllowRestart = true;
- private final OpenFdMonitor mOpenFdMonitor;
private final List<Integer> mInterestingJavaPids = new ArrayList<>();
/**
@@ -349,8 +338,6 @@
// Initialize monitor for Binder threads.
addMonitor(new BinderThreadMonitor());
- mOpenFdMonitor = OpenFdMonitor.create();
-
mInterestingJavaPids.add(Process.myPid());
// See the notes on DEFAULT_TIMEOUT.
@@ -602,40 +589,30 @@
timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);
}
- boolean fdLimitTriggered = false;
- if (mOpenFdMonitor != null) {
- fdLimitTriggered = mOpenFdMonitor.monitor();
- }
-
- if (!fdLimitTriggered) {
- final int waitState = evaluateCheckerCompletionLocked();
- if (waitState == COMPLETED) {
- // The monitors have returned; reset
- waitedHalf = false;
- continue;
- } else if (waitState == WAITING) {
- // still waiting but within their configured intervals; back off and recheck
- continue;
- } else if (waitState == WAITED_HALF) {
- if (!waitedHalf) {
- Slog.i(TAG, "WAITED_HALF");
- // We've waited half the deadlock-detection interval. Pull a stack
- // trace and wait another half.
- ArrayList<Integer> pids = new ArrayList<>(mInterestingJavaPids);
- ActivityManagerService.dumpStackTraces(pids, null, null,
- getInterestingNativePids(), null);
- waitedHalf = true;
- }
- continue;
+ final int waitState = evaluateCheckerCompletionLocked();
+ if (waitState == COMPLETED) {
+ // The monitors have returned; reset
+ waitedHalf = false;
+ continue;
+ } else if (waitState == WAITING) {
+ // still waiting but within their configured intervals; back off and recheck
+ continue;
+ } else if (waitState == WAITED_HALF) {
+ if (!waitedHalf) {
+ Slog.i(TAG, "WAITED_HALF");
+ // We've waited half the deadlock-detection interval. Pull a stack
+ // trace and wait another half.
+ ArrayList<Integer> pids = new ArrayList<>(mInterestingJavaPids);
+ ActivityManagerService.dumpStackTraces(pids, null, null,
+ getInterestingNativePids(), null);
+ waitedHalf = true;
}
-
- // something is overdue!
- blockedCheckers = getBlockedCheckersLocked();
- subject = describeCheckersLocked(blockedCheckers);
- } else {
- blockedCheckers = Collections.emptyList();
- subject = "Open FD high water mark reached";
+ continue;
}
+
+ // something is overdue!
+ blockedCheckers = getBlockedCheckersLocked();
+ subject = describeCheckersLocked(blockedCheckers);
allowRestart = mAllowRestart;
}
@@ -738,94 +715,4 @@
Slog.w(TAG, "Failed to write to /proc/sysrq-trigger", e);
}
}
-
- public static final class OpenFdMonitor {
- /**
- * Number of FDs below the soft limit that we trigger a runtime restart at. This was
- * chosen arbitrarily, but will need to be at least 6 in order to have a sufficient number
- * of FDs in reserve to complete a dump.
- */
- private static final int FD_HIGH_WATER_MARK = 12;
-
- private final File mDumpDir;
- private final File mFdHighWaterMark;
-
- public static OpenFdMonitor create() {
- // Only run the FD monitor on debuggable builds (such as userdebug and eng builds).
- if (!Build.IS_DEBUGGABLE) {
- return null;
- }
-
- final StructRlimit rlimit;
- try {
- rlimit = android.system.Os.getrlimit(OsConstants.RLIMIT_NOFILE);
- } catch (ErrnoException errno) {
- Slog.w(TAG, "Error thrown from getrlimit(RLIMIT_NOFILE)", errno);
- return null;
- }
-
- // The assumption we're making here is that FD numbers are allocated (more or less)
- // sequentially, which is currently (and historically) true since open is currently
- // specified to always return the lowest-numbered non-open file descriptor for the
- // current process.
- //
- // We do this to avoid having to enumerate the contents of /proc/self/fd in order to
- // count the number of descriptors open in the process.
- final File fdThreshold = new File("/proc/self/fd/" + (rlimit.rlim_cur - FD_HIGH_WATER_MARK));
- return new OpenFdMonitor(new File("/data/anr"), fdThreshold);
- }
-
- OpenFdMonitor(File dumpDir, File fdThreshold) {
- mDumpDir = dumpDir;
- mFdHighWaterMark = fdThreshold;
- }
-
- /**
- * Dumps open file descriptors and their full paths to a temporary file in {@code mDumpDir}.
- */
- private void dumpOpenDescriptors() {
- // We cannot exec lsof to get more info about open file descriptors because a newly
- // forked process will not have the permissions to readlink. Instead list all open
- // descriptors from /proc/pid/fd and resolve them.
- List<String> dumpInfo = new ArrayList<>();
- String fdDirPath = String.format("/proc/%d/fd/", Process.myPid());
- File[] fds = new File(fdDirPath).listFiles();
- if (fds == null) {
- dumpInfo.add("Unable to list " + fdDirPath);
- } else {
- for (File f : fds) {
- String fdSymLink = f.getAbsolutePath();
- String resolvedPath = "";
- try {
- resolvedPath = Os.readlink(fdSymLink);
- } catch (ErrnoException ex) {
- resolvedPath = ex.getMessage();
- }
- dumpInfo.add(fdSymLink + "\t" + resolvedPath);
- }
- }
-
- // Dump the fds & paths to a temp file.
- try {
- File dumpFile = File.createTempFile("anr_fd_", "", mDumpDir);
- Path out = Paths.get(dumpFile.getAbsolutePath());
- Files.write(out, dumpInfo, StandardCharsets.UTF_8);
- } catch (IOException ex) {
- Slog.w(TAG, "Unable to write open descriptors to file: " + ex);
- }
- }
-
- /**
- * @return {@code true} if the high water mark was breached and a dump was written,
- * {@code false} otherwise.
- */
- public boolean monitor() {
- if (mFdHighWaterMark.exists()) {
- dumpOpenDescriptors();
- return true;
- }
-
- return false;
- }
- }
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 3f867f6..33a92e6 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -215,7 +215,7 @@
* Watch for apps being put into forced app standby, so we can step their fg
* services down.
*/
- class ForcedStandbyListener extends AppStateTracker.Listener {
+ class ForcedStandbyListener implements AppStateTracker.ServiceStateListener {
@Override
public void stopForegroundServicesForUidPackage(final int uid, final String packageName) {
synchronized (mAm) {
@@ -403,7 +403,7 @@
void systemServicesReady() {
AppStateTracker ast = LocalServices.getService(AppStateTracker.class);
- ast.addListener(new ForcedStandbyListener());
+ ast.addServiceStateListener(new ForcedStandbyListener());
mAppWidgetManagerInternal = LocalServices.getService(AppWidgetManagerInternal.class);
setWhiteListAllowWhileInUsePermissionInFgs();
}
@@ -690,7 +690,7 @@
}
if (allowBackgroundActivityStarts) {
- r.whitelistBgActivityStartsOnServiceStart();
+ r.allowBgActivityStartsOnServiceStart();
}
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
@@ -2045,7 +2045,7 @@
s.whitelistManager = true;
}
if ((flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
- s.setHasBindingWhitelistingBgActivityStarts(true);
+ s.setAllowedBgActivityStartsByBinding(true);
}
if (s.app != null) {
updateServiceClientActivitiesLocked(s.app, c, true);
@@ -3457,9 +3457,9 @@
updateWhitelistManagerLocked(s.app);
}
}
- // And do the same for bg activity starts whitelisting.
+ // And do the same for bg activity starts ability.
if ((c.flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
- s.updateHasBindingWhitelistingBgActivityStarts();
+ s.updateIsAllowedBgActivityStartsByBinding();
}
if (s.app != null) {
updateServiceClientActivitiesLocked(s.app, c, true);
@@ -4881,12 +4881,9 @@
if (instr != null && instr.mHasBackgroundActivityStartsPermission) {
return true;
}
- }
-
- final boolean hasAllowBackgroundActivityStartsToken = r.app != null
- ? !r.app.mAllowBackgroundActivityStartsTokens.isEmpty() : false;
- if (hasAllowBackgroundActivityStartsToken) {
- return true;
+ if (r.app.areBackgroundActivityStartsAllowedByToken()) {
+ return true;
+ }
}
if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 09ed16e..775119c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -253,7 +253,8 @@
// allowing the next pending start to run.
public long BG_START_TIMEOUT = DEFAULT_BG_START_TIMEOUT;
- // For how long after a whitelisted service's start its process can start a background activity
+ // For a service that has been allowed to start background activities, how long after it started
+ // its process can start a background activity.
public long SERVICE_BG_ACTIVITY_START_TIMEOUT = DEFAULT_SERVICE_BG_ACTIVITY_START_TIMEOUT;
// Initial backoff delay for retrying bound foreground services
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index f872c6b..171b20c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -81,7 +81,6 @@
static final String POSTFIX_PROCESS_OBSERVERS = (APPEND_CATEGORY_NAME)
? "_ProcessObservers" : "";
static final String POSTFIX_PROCESSES = (APPEND_CATEGORY_NAME) ? "_Processes" : "";
- static final String POSTFIX_PROVIDER = (APPEND_CATEGORY_NAME) ? "_Provider" : "";
static final String POSTFIX_PSS = (APPEND_CATEGORY_NAME) ? "_Pss" : "";
static final String POSTFIX_SERVICE = (APPEND_CATEGORY_NAME) ? "_Service" : "";
static final String POSTFIX_SERVICE_EXECUTING =
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index fa18ccb..cca2654 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -21,20 +21,16 @@
import static android.Manifest.permission.FILTER_EVENTS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.Manifest.permission.REMOVE_TASKS;
import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS;
-import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.AppOpsManager.OP_NONE;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
-import static android.content.pm.PackageManager.GET_PROVIDERS;
import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES;
import static android.content.pm.PackageManager.MATCH_ALL;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
@@ -54,9 +50,7 @@
import static android.os.Process.NETWORK_STACK_UID;
import static android.os.Process.NFC_UID;
import static android.os.Process.PHONE_UID;
-import static android.os.Process.PROC_CHAR;
import static android.os.Process.PROC_OUT_LONG;
-import static android.os.Process.PROC_PARENS;
import static android.os.Process.PROC_SPACE_TERM;
import static android.os.Process.ROOT_UID;
import static android.os.Process.SCHED_FIFO;
@@ -98,11 +92,9 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
@@ -117,7 +109,6 @@
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_POWER;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESSES;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESS_OBSERVERS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROVIDER;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS;
@@ -195,10 +186,8 @@
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
import android.content.ContentCaptureOptions;
-import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.IContentProvider;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
@@ -217,7 +206,6 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser;
import android.content.pm.ParceledListSlice;
-import android.content.pm.PathPermission;
import android.content.pm.PermissionInfo;
import android.content.pm.ProcessInfo;
import android.content.pm.ProviderInfo;
@@ -291,6 +279,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 +299,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;
@@ -348,7 +338,6 @@
import com.android.server.LockGuard;
import com.android.server.NetworkManagementInternal;
import com.android.server.PackageWatchdog;
-import com.android.server.RescueParty;
import com.android.server.ServiceThread;
import com.android.server.SystemConfig;
import com.android.server.SystemService;
@@ -403,7 +392,6 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
@@ -429,13 +417,12 @@
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
static final String TAG_LRU = TAG + POSTFIX_LRU;
- private static final String TAG_MU = TAG + POSTFIX_MU;
+ static final String TAG_MU = TAG + POSTFIX_MU;
static final String TAG_NETWORK = TAG + POSTFIX_NETWORK;
static final String TAG_OOM_ADJ = TAG + POSTFIX_OOM_ADJ;
private static final String TAG_POWER = TAG + POSTFIX_POWER;
static final String TAG_PROCESS_OBSERVERS = TAG + POSTFIX_PROCESS_OBSERVERS;
static final String TAG_PROCESSES = TAG + POSTFIX_PROCESSES;
- private static final String TAG_PROVIDER = TAG + POSTFIX_PROVIDER;
static final String TAG_PSS = TAG + POSTFIX_PSS;
private static final String TAG_SERVICE = TAG + POSTFIX_SERVICE;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
@@ -657,6 +644,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.
@@ -1134,53 +1129,10 @@
@GuardedBy("this")
final SparseArray<BackupRecord> mBackupTargets = new SparseArray<>();
- final ProviderMap mProviderMap;
-
- /**
- * List of content providers who have clients waiting for them. The
- * application is currently being launched and the provider will be
- * removed from this list once it is published.
- */
- final ArrayList<ContentProviderRecord> mLaunchingProviders = new ArrayList<>();
-
- boolean mSystemProvidersInstalled;
+ final ContentProviderHelper mCpHelper;
CoreSettingsObserver mCoreSettingsObserver;
- DevelopmentSettingsObserver mDevelopmentSettingsObserver;
-
- private final class DevelopmentSettingsObserver extends ContentObserver {
- private final Uri mUri = Settings.Global
- .getUriFor(Settings.Global.DEVELOPMENT_SETTINGS_ENABLED);
-
- private final ComponentName mBugreportStorageProvider = new ComponentName(
- "com.android.shell", "com.android.shell.BugreportStorageProvider");
-
- public DevelopmentSettingsObserver() {
- super(mHandler);
- mContext.getContentResolver().registerContentObserver(mUri, false, this,
- UserHandle.USER_ALL);
- // Always kick once to ensure that we match current state
- onChange();
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
- if (mUri.equals(uri)) {
- onChange();
- }
- }
-
- public void onChange() {
- final boolean enabled = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, Build.IS_ENG ? 1 : 0) != 0;
- mContext.getPackageManager().setComponentEnabledSetting(mBugreportStorageProvider,
- enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
- : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
- 0);
- }
- }
-
/**
* Thread-local storage used to carry caller permissions over through
* indirect content-provider access.
@@ -1817,7 +1769,7 @@
case CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG: {
ProcessRecord app = (ProcessRecord)msg.obj;
synchronized (ActivityManagerService.this) {
- processContentProviderPublishTimedOutLocked(app);
+ mCpHelper.processContentProviderPublishTimedOutLocked(app);
}
} break;
case KILL_APPLICATION_MSG: {
@@ -2053,7 +2005,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 +2021,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 +2111,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) {
@@ -2548,7 +2512,7 @@
? new IntentFirewall(new IntentFirewallInterface(), mHandler) : null;
mProcessCpuThread = null;
mProcessStats = null;
- mProviderMap = null;
+ mCpHelper = new ContentProviderHelper(this, false);
// For the usage of {@link ActiveServices#cleanUpServices} that may be invoked from
// {@link ActivityStackSupervisor#cleanUpRemovedTaskLocked}.
mServices = hasHandlerThread ? new ActiveServices(this) : null;
@@ -2629,7 +2593,7 @@
mBroadcastQueues[2] = mOffloadBroadcastQueue;
mServices = new ActiveServices(this);
- mProviderMap = new ProviderMap(this);
+ mCpHelper = new ContentProviderHelper(this, true);
mPackageWatchdog = PackageWatchdog.getInstance(mUiContext);
mAppErrors = new AppErrors(mUiContext, this, mPackageWatchdog);
@@ -4303,11 +4267,11 @@
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent,
null, null, 0, null, null, permission.ACCESS_INSTANT_APPS, null,
- false, false, resolvedUserId, false);
+ false, false, resolvedUserId, false, null);
} else {
broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent,
null, null, 0, null, null, null, null, false, false, resolvedUserId,
- false);
+ false, null);
}
if (observer != null) {
@@ -4832,10 +4796,10 @@
// Clean-up disabled providers.
ArrayList<ContentProviderRecord> providers = new ArrayList<>();
- mProviderMap.collectPackageProvidersLocked(
+ mCpHelper.getProviderMap().collectPackageProvidersLocked(
packageName, disabledClasses, true, false, userId, providers);
for (int i = providers.size() - 1; i >= 0; i--) {
- removeDyingProviderLocked(null, providers.get(i), true);
+ mCpHelper.removeDyingProviderLocked(null, providers.get(i), true);
}
// Clean-up disabled broadcast receivers.
@@ -4900,7 +4864,13 @@
mAppErrors.resetProcessCrashTimeLocked(packageName == null, appId, userId);
}
- boolean didSomething = mProcessList.killPackageProcessesLocked(packageName, appId, userId,
+ // Notify first that the package is stopped, so its process won't be restarted unexpectedly
+ // if there is an activity of the package without attached process becomes visible when
+ // killing its other processes with visible activities.
+ boolean didSomething =
+ mAtmInternal.onForceStopPackage(packageName, doit, evenPersistent, userId);
+
+ didSomething |= mProcessList.killPackageProcessesLocked(packageName, appId, userId,
ProcessList.INVALID_ADJ, callerWillRestart, false /* allowRestart */, doit,
evenPersistent, true /* setRemoved */,
packageName == null ? ApplicationExitInfo.REASON_USER_STOPPED
@@ -4909,9 +4879,6 @@
(packageName == null ? ("stop user " + userId) : ("stop " + packageName))
+ " due to " + reason);
- didSomething |=
- mAtmInternal.onForceStopPackage(packageName, doit, evenPersistent, userId);
-
if (mServices.bringDownDisabledPackageServicesLocked(
packageName, null /* filterByClasses */, userId, evenPersistent, doit)) {
if (!doit) {
@@ -4926,15 +4893,15 @@
}
ArrayList<ContentProviderRecord> providers = new ArrayList<>();
- if (mProviderMap.collectPackageProvidersLocked(packageName, null, doit, evenPersistent,
- userId, providers)) {
+ if (mCpHelper.getProviderMap().collectPackageProvidersLocked(packageName, null, doit,
+ evenPersistent, userId, providers)) {
if (!doit) {
return true;
}
didSomething = true;
}
for (i = providers.size() - 1; i >= 0; i--) {
- removeDyingProviderLocked(null, providers.get(i), true);
+ mCpHelper.removeDyingProviderLocked(null, providers.get(i), true);
}
// Remove transient permissions granted from/to this package/user
@@ -4968,15 +4935,6 @@
}
@GuardedBy("this")
- private final void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
- cleanupAppInLaunchingProvidersLocked(app, true);
- mProcessList.removeProcessLocked(app, false, true,
- ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
- ApplicationExitInfo.SUBREASON_UNKNOWN,
- "timeout publishing content providers");
- }
-
- @GuardedBy("this")
private final void processStartTimedOutLocked(ProcessRecord app) {
final int pid = app.pid;
boolean gone = removePidIfNoThread(app);
@@ -4988,7 +4946,7 @@
mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
// Take care of any launching providers waiting for this process.
- cleanupAppInLaunchingProvidersLocked(app, true);
+ mCpHelper.cleanupAppInLaunchingProvidersLocked(app, true);
// Take care of any services that are waiting for the process.
mServices.processStartTimedOutLocked(app);
app.kill("start timeout", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE, true);
@@ -5136,9 +5094,11 @@
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
- List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
+ List<ProviderInfo> providers = normalMode
+ ? mCpHelper.generateApplicationProvidersLocked(app)
+ : null;
- if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
+ if (providers != null && mCpHelper.checkAppInLaunchingProvidersLocked(app)) {
Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
msg.obj = app;
mHandler.sendMessageDelayed(msg,
@@ -5458,6 +5418,14 @@
}
}
+ void checkTime(long startTime, String where) {
+ long now = SystemClock.uptimeMillis();
+ if ((now - startTime) > 50) {
+ // If we are taking more than 50ms, log about it.
+ Slog.w(TAG, "Slow operation: " + (now - startTime) + "ms so far, now at " + where);
+ }
+ }
+
@Override
public void showBootMessage(final CharSequence msg, final boolean always) {
if (Binder.getCallingUid() != myUid()) {
@@ -6442,22 +6410,6 @@
return ptw != null ? ptw.tag : null;
}
- private ProviderInfo getProviderInfoLocked(String authority, @UserIdInt int userId,
- int pmFlags) {
- ProviderInfo pi = null;
- ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userId);
- if (cpr != null) {
- pi = cpr.info;
- } else {
- try {
- pi = AppGlobals.getPackageManager().resolveContentProvider(
- authority, PackageManager.GET_URI_PERMISSION_PATTERNS | pmFlags, userId);
- } catch (RemoteException ex) {
- }
- }
- return pi;
- }
-
@VisibleForTesting
public void grantImplicitAccess(int userId, Intent intent, int visibleUid, int recipientAppId) {
getPackageManagerInternalLocked().
@@ -6549,7 +6501,7 @@
}
final String authority = uri.getAuthority();
- final ProviderInfo pi = getProviderInfoLocked(authority, userId,
+ final ProviderInfo pi = mCpHelper.getProviderInfoLocked(authority, userId,
MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
if (pi == null) {
Slog.w(TAG, "No content provider found for permission revoke: "
@@ -6727,803 +6679,6 @@
mActivityTaskManager.startSystemLockTaskMode(taskId);
}
- // =========================================================
- // CONTENT PROVIDERS
- // =========================================================
-
- private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
- List<ProviderInfo> providers = null;
- try {
- providers = AppGlobals.getPackageManager()
- .queryContentProviders(app.processName, app.uid,
- STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS
- | MATCH_DEBUG_TRIAGED_MISSING, /*metadastaKey=*/ null)
- .getList();
- } catch (RemoteException ex) {
- }
- if (DEBUG_MU) Slog.v(TAG_MU,
- "generateApplicationProvidersLocked, app.info.uid = " + app.uid);
- int userId = app.userId;
- if (providers != null) {
- int N = providers.size();
- app.pubProviders.ensureCapacity(N + app.pubProviders.size());
- for (int i=0; i<N; i++) {
- // TODO: keep logic in sync with installEncryptionUnawareProviders
- ProviderInfo cpi =
- (ProviderInfo)providers.get(i);
- boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
- cpi.name, cpi.flags);
- if (singleton && UserHandle.getUserId(app.uid) != UserHandle.USER_SYSTEM) {
- // This is a singleton provider, but a user besides the
- // default user is asking to initialize a process it runs
- // in... well, no, it doesn't actually run in this process,
- // it runs in the process of the default user. Get rid of it.
- providers.remove(i);
- N--;
- i--;
- continue;
- }
-
- ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
- ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
- if (cpr == null) {
- cpr = new ContentProviderRecord(this, cpi, app.info, comp, singleton);
- mProviderMap.putProviderByClass(comp, cpr);
- }
- if (DEBUG_MU) Slog.v(TAG_MU,
- "generateApplicationProvidersLocked, cpi.uid = " + cpr.uid);
- app.pubProviders.put(cpi.name, cpr);
- if (!cpi.multiprocess || !"android".equals(cpi.packageName)) {
- // Don't add this if it is a platform component that is marked
- // to run in multiple processes, because this is actually
- // part of the framework so doesn't make sense to track as a
- // separate apk in the process.
- app.addPackage(cpi.applicationInfo.packageName,
- cpi.applicationInfo.longVersionCode, mProcessStats);
- }
- notifyPackageUse(cpi.applicationInfo.packageName,
- PackageManager.NOTIFY_PACKAGE_USE_CONTENT_PROVIDER);
- }
- }
- return providers;
- }
-
- /**
- * Check if the calling UID has a possible chance at accessing the provider
- * at the given authority and user.
- */
- public String checkContentProviderAccess(String authority, int userId) {
- if (userId == UserHandle.USER_ALL) {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.INTERACT_ACROSS_USERS_FULL, TAG);
- userId = UserHandle.getCallingUserId();
- }
-
- ProviderInfo cpi = null;
- try {
- cpi = AppGlobals.getPackageManager().resolveContentProvider(authority,
- STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS
- | PackageManager.MATCH_DISABLED_COMPONENTS
- | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- userId);
- } catch (RemoteException ignored) {
- }
- if (cpi == null) {
- return "Failed to find provider " + authority + " for user " + userId
- + "; expected to find a valid ContentProvider for this authority";
- }
-
- ProcessRecord r = null;
- synchronized (mPidsSelfLocked) {
- r = mPidsSelfLocked.get(Binder.getCallingPid());
- }
- if (r == null) {
- return "Failed to find PID " + Binder.getCallingPid();
- }
-
- synchronized (this) {
- return checkContentProviderPermissionLocked(cpi, r, userId, true);
- }
- }
-
- /**
- * Check if {@link ProcessRecord} has a possible chance at accessing the
- * given {@link ProviderInfo}. Final permission checking is always done
- * in {@link ContentProvider}.
- */
- private final String checkContentProviderPermissionLocked(
- ProviderInfo cpi, ProcessRecord r, int userId, boolean checkUser) {
- final int callingPid = (r != null) ? r.pid : Binder.getCallingPid();
- final int callingUid = (r != null) ? r.uid : Binder.getCallingUid();
- boolean checkedGrants = false;
- if (checkUser) {
- // Looking for cross-user grants before enforcing the typical cross-users permissions
- int tmpTargetUserId = mUserController.unsafeConvertIncomingUser(userId);
- if (tmpTargetUserId != UserHandle.getUserId(callingUid)) {
- if (mUgmInternal.checkAuthorityGrants(
- callingUid, cpi, tmpTargetUserId, checkUser)) {
- return null;
- }
- checkedGrants = true;
- }
- userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
- ALLOW_NON_FULL, "checkContentProviderPermissionLocked " + cpi.authority, null);
- if (userId != tmpTargetUserId) {
- // When we actually went to determine the final targer user ID, this ended
- // up different than our initial check for the authority. This is because
- // they had asked for USER_CURRENT_OR_SELF and we ended up switching to
- // SELF. So we need to re-check the grants again.
- checkedGrants = false;
- }
- }
- if (checkComponentPermission(cpi.readPermission, callingPid, callingUid,
- cpi.applicationInfo.uid, cpi.exported)
- == PackageManager.PERMISSION_GRANTED) {
- return null;
- }
- if (checkComponentPermission(cpi.writePermission, callingPid, callingUid,
- cpi.applicationInfo.uid, cpi.exported)
- == PackageManager.PERMISSION_GRANTED) {
- return null;
- }
-
- PathPermission[] pps = cpi.pathPermissions;
- if (pps != null) {
- int i = pps.length;
- while (i > 0) {
- i--;
- PathPermission pp = pps[i];
- String pprperm = pp.getReadPermission();
- if (pprperm != null && checkComponentPermission(pprperm, callingPid, callingUid,
- cpi.applicationInfo.uid, cpi.exported)
- == PackageManager.PERMISSION_GRANTED) {
- return null;
- }
- String ppwperm = pp.getWritePermission();
- if (ppwperm != null && checkComponentPermission(ppwperm, callingPid, callingUid,
- cpi.applicationInfo.uid, cpi.exported)
- == PackageManager.PERMISSION_GRANTED) {
- return null;
- }
- }
- }
- if (!checkedGrants
- && mUgmInternal.checkAuthorityGrants(callingUid, cpi, userId, checkUser)) {
- return null;
- }
-
- final String suffix;
- if (!cpi.exported) {
- suffix = " that is not exported from UID " + cpi.applicationInfo.uid;
- } else if (android.Manifest.permission.MANAGE_DOCUMENTS.equals(cpi.readPermission)) {
- suffix = " requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs";
- } else {
- suffix = " requires " + cpi.readPermission + " or " + cpi.writePermission;
- }
- final String msg = "Permission Denial: opening provider " + cpi.name
- + " from " + (r != null ? r : "(null)") + " (pid=" + callingPid
- + ", uid=" + callingUid + ")" + suffix;
- Slog.w(TAG, msg);
- return msg;
- }
-
- @GuardedBy("this")
- private ContentProviderConnection incProviderCountLocked(ProcessRecord r,
- final ContentProviderRecord cpr, IBinder externalProcessToken, int callingUid,
- String callingPackage, String callingTag, boolean stable,
- boolean updateLru, long startTime) {
- if (r != null) {
- for (int i=0; i<r.conProviders.size(); i++) {
- ContentProviderConnection conn = r.conProviders.get(i);
- if (conn.provider == cpr) {
- conn.incrementCount(stable);
- return conn;
- }
- }
-
- // Create a new ContentProviderConnection. The reference count
- // is known to be 1.
- ContentProviderConnection conn = new ContentProviderConnection(cpr, r, callingPackage);
- conn.startAssociationIfNeeded();
- conn.initializeCount(stable);
- cpr.connections.add(conn);
- r.conProviders.add(conn);
- startAssociationLocked(r.uid, r.processName, r.getCurProcState(),
- cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
- if (updateLru && cpr.proc != null
- && r != null && r.setAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
- // If this is a perceptible app accessing the provider, make
- // sure to count it as being accessed and thus back up on
- // the LRU list. This is good because content providers are
- // often expensive to start. The calls to checkTime() use
- // the "getContentProviderImpl" tag here, because it's part
- // of the checktime log in getContentProviderImpl().
- checkTime(startTime, "getContentProviderImpl: before updateLruProcess");
- mProcessList.updateLruProcessLocked(cpr.proc, false, null);
- checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
- }
- return conn;
- }
- cpr.addExternalProcessHandleLocked(externalProcessToken, callingUid, callingTag);
- return null;
- }
-
- @GuardedBy("this")
- private boolean decProviderCountLocked(ContentProviderConnection conn,
- ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
- if (conn != null) {
- cpr = conn.provider;
- final int referenceCount = conn.decrementCount(stable);
- if (referenceCount == 0) {
- conn.stopAssociation();
- cpr.connections.remove(conn);
- conn.client.conProviders.remove(conn);
- if (conn.client.setProcState < PROCESS_STATE_LAST_ACTIVITY) {
- // The client is more important than last activity -- note the time this
- // is happening, so we keep the old provider process around a bit as last
- // activity to avoid thrashing it.
- if (cpr.proc != null) {
- cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
- }
- }
- stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
- cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
- return true;
- }
- return false;
- }
- cpr.removeExternalProcessHandleLocked(externalProcessToken);
- return false;
- }
-
- void checkTime(long startTime, String where) {
- long now = SystemClock.uptimeMillis();
- if ((now-startTime) > 50) {
- // If we are taking more than 50ms, log about it.
- Slog.w(TAG, "Slow operation: " + (now-startTime) + "ms so far, now at " + where);
- }
- }
-
- private static final int[] PROCESS_STATE_STATS_FORMAT = new int[] {
- PROC_SPACE_TERM,
- PROC_SPACE_TERM|PROC_PARENS,
- PROC_SPACE_TERM|PROC_CHAR|PROC_OUT_LONG, // 3: process state
- };
-
- private final long[] mProcessStateStatsLongs = new long[1];
-
- boolean isProcessAliveLocked(ProcessRecord proc) {
- if (proc.pid <= 0) {
- if (DEBUG_OOM_ADJ) Slog.d(TAG, "Process hasn't started yet: " + proc);
- return false;
- }
- if (proc.procStatFile == null) {
- proc.procStatFile = "/proc/" + proc.pid + "/stat";
- }
- mProcessStateStatsLongs[0] = 0;
- if (!readProcFile(proc.procStatFile, PROCESS_STATE_STATS_FORMAT, null,
- mProcessStateStatsLongs, null)) {
- if (DEBUG_OOM_ADJ) Slog.d(TAG, "UNABLE TO RETRIEVE STATE FOR " + proc.procStatFile);
- return false;
- }
- final long state = mProcessStateStatsLongs[0];
- if (DEBUG_OOM_ADJ) Slog.d(TAG, "RETRIEVED STATE FOR " + proc.procStatFile + ": "
- + (char)state);
- if (state != 'Z' && state != 'X' && state != 'x' && state != 'K') {
- return Process.getUidForPid(proc.pid) == proc.uid;
- }
- return false;
- }
-
- private String checkContentProviderAssociation(ProcessRecord callingApp, int callingUid,
- ProviderInfo cpi) {
- if (callingApp == null) {
- return validateAssociationAllowedLocked(cpi.packageName, cpi.applicationInfo.uid,
- null, callingUid) ? null : "<null>";
- }
- for (int i = callingApp.pkgList.size() - 1; i >= 0; i--) {
- if (!validateAssociationAllowedLocked(callingApp.pkgList.keyAt(i), callingApp.uid,
- cpi.packageName, cpi.applicationInfo.uid)) {
- return cpi.packageName;
- }
- }
- return null;
- }
-
- private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
- String name, IBinder token, int callingUid, String callingPackage, String callingTag,
- boolean stable, int userId) {
- ContentProviderRecord cpr;
- ContentProviderConnection conn = null;
- ProviderInfo cpi = null;
- boolean providerRunning = false;
-
- synchronized(this) {
- long startTime = SystemClock.uptimeMillis();
-
- ProcessRecord r = null;
- if (caller != null) {
- r = getRecordForAppLocked(caller);
- if (r == null) {
- throw new SecurityException(
- "Unable to find app for caller " + caller
- + " (pid=" + Binder.getCallingPid()
- + ") when getting content provider " + name);
- }
- }
-
- boolean checkCrossUser = true;
-
- checkTime(startTime, "getContentProviderImpl: getProviderByName");
-
- // First check if this content provider has been published...
- cpr = mProviderMap.getProviderByName(name, userId);
- // If that didn't work, check if it exists for user 0 and then
- // verify that it's a singleton provider before using it.
- if (cpr == null && userId != UserHandle.USER_SYSTEM) {
- cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);
- if (cpr != null) {
- cpi = cpr.info;
- if (isSingleton(cpi.processName, cpi.applicationInfo,
- cpi.name, cpi.flags)
- && isValidSingletonCall(r == null ? callingUid : r.uid,
- cpi.applicationInfo.uid)) {
- userId = UserHandle.USER_SYSTEM;
- checkCrossUser = false;
- } else {
- cpr = null;
- cpi = null;
- }
- }
- }
-
- ProcessRecord dyingProc = null;
- if (cpr != null && cpr.proc != null) {
- providerRunning = !cpr.proc.killed;
-
- // Note if killedByAm is also set, this means the provider process has just been
- // killed by AM (in ProcessRecord.kill()), but appDiedLocked() hasn't been called
- // yet. So we need to call appDiedLocked() here and let it clean up.
- // (See the commit message on I2c4ba1e87c2d47f2013befff10c49b3dc337a9a7 to see
- // how to test this case.)
- if (cpr.proc.killed && cpr.proc.killedByAm) {
- Slog.wtf(TAG, cpr.proc.toString() + " was killed by AM but isn't really dead");
- // Now we are going to wait for the death before starting the new process.
- dyingProc = cpr.proc;
- }
- }
-
- if (providerRunning) {
- cpi = cpr.info;
- String msg;
-
- if (r != null && cpr.canRunHere(r)) {
- if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) {
- throw new SecurityException("Content provider lookup "
- + cpr.name.flattenToShortString()
- + " failed: association not allowed with package " + msg);
- }
- checkTime(startTime,
- "getContentProviderImpl: before checkContentProviderPermission");
- if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
- != null) {
- throw new SecurityException(msg);
- }
- checkTime(startTime,
- "getContentProviderImpl: after checkContentProviderPermission");
-
- // This provider has been published or is in the process
- // of being published... but it is also allowed to run
- // in the caller's process, so don't make a connection
- // and just let the caller instantiate its own instance.
- ContentProviderHolder holder = cpr.newHolder(null);
- // don't give caller the provider object, it needs
- // to make its own.
- holder.provider = null;
- return holder;
- }
-
- // Don't expose providers between normal apps and instant apps
- try {
- if (AppGlobals.getPackageManager()
- .resolveContentProvider(name, 0 /*flags*/, userId) == null) {
- return null;
- }
- } catch (RemoteException e) {
- }
-
- if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) {
- throw new SecurityException("Content provider lookup "
- + cpr.name.flattenToShortString()
- + " failed: association not allowed with package " + msg);
- }
- checkTime(startTime,
- "getContentProviderImpl: before checkContentProviderPermission");
- if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
- != null) {
- throw new SecurityException(msg);
- }
- checkTime(startTime,
- "getContentProviderImpl: after checkContentProviderPermission");
-
- final long origId = Binder.clearCallingIdentity();
-
- checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");
-
- // In this case the provider instance already exists, so we can
- // return it right away.
- conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage,
- callingTag, stable, true, startTime);
-
- checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
- final int verifiedAdj = cpr.proc.verifiedAdj;
- boolean success = updateOomAdjLocked(cpr.proc, true,
- OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
- // XXX things have changed so updateOomAdjLocked doesn't actually tell us
- // if the process has been successfully adjusted. So to reduce races with
- // it, we will check whether the process still exists. Note that this doesn't
- // completely get rid of races with LMK killing the process, but should make
- // them much smaller.
- if (success && verifiedAdj != cpr.proc.setAdj && !isProcessAliveLocked(cpr.proc)) {
- success = false;
- }
- maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
- checkTime(startTime, "getContentProviderImpl: after updateOomAdj");
- if (DEBUG_PROVIDER) Slog.i(TAG_PROVIDER, "Adjust success: " + success);
- // NOTE: there is still a race here where a signal could be
- // pending on the process even though we managed to update its
- // adj level. Not sure what to do about this, but at least
- // the race is now smaller.
- if (!success) {
- // Uh oh... it looks like the provider's process
- // has been killed on us. We need to wait for a new
- // process to be started, and make sure its death
- // doesn't kill our process.
- Slog.wtf(TAG, "Existing provider " + cpr.name.flattenToShortString()
- + " is crashing; detaching " + r);
- boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
- if (!lastRef) {
- // This wasn't the last ref our process had on
- // the provider... we will be killed during cleaning up, bail.
- return null;
- }
- // We'll just start a new process to host the content provider
- providerRunning = false;
- conn = null;
- dyingProc = cpr.proc;
- } else {
- cpr.proc.verifiedAdj = cpr.proc.setAdj;
- }
-
- Binder.restoreCallingIdentity(origId);
- }
-
- if (!providerRunning) {
- try {
- checkTime(startTime, "getContentProviderImpl: before resolveContentProvider");
- cpi = AppGlobals.getPackageManager().
- resolveContentProvider(name,
- STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
- checkTime(startTime, "getContentProviderImpl: after resolveContentProvider");
- } catch (RemoteException ex) {
- }
- if (cpi == null) {
- return null;
- }
- // If the provider is a singleton AND
- // (it's a call within the same user || the provider is a
- // privileged app)
- // Then allow connecting to the singleton provider
- boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
- cpi.name, cpi.flags)
- && isValidSingletonCall(r == null ? callingUid : r.uid,
- cpi.applicationInfo.uid);
- if (singleton) {
- userId = UserHandle.USER_SYSTEM;
- }
- cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
- checkTime(startTime, "getContentProviderImpl: got app info for user");
-
- String msg;
- if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) {
- throw new SecurityException("Content provider lookup " + name
- + " failed: association not allowed with package " + msg);
- }
- checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission");
- if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton))
- != null) {
- throw new SecurityException(msg);
- }
- checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission");
-
- if (!mProcessesReady
- && !cpi.processName.equals("system")) {
- // If this content provider does not run in the system
- // process, and the system is not yet ready to run other
- // processes, then fail fast instead of hanging.
- throw new IllegalArgumentException(
- "Attempt to launch content provider before system ready");
- }
-
- // If system providers are not installed yet we aggressively crash to avoid
- // creating multiple instance of these providers and then bad things happen!
- if (!mSystemProvidersInstalled && cpi.applicationInfo.isSystemApp()
- && "system".equals(cpi.processName)) {
- throw new IllegalStateException("Cannot access system provider: '"
- + cpi.authority + "' before system providers are installed!");
- }
-
- // Make sure that the user who owns this provider is running. If not,
- // we don't want to allow it to run.
- if (!mUserController.isUserRunning(userId, 0)) {
- Slog.w(TAG, "Unable to launch app "
- + cpi.applicationInfo.packageName + "/"
- + cpi.applicationInfo.uid + " for provider "
- + name + ": user " + userId + " is stopped");
- return null;
- }
-
- ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
- checkTime(startTime, "getContentProviderImpl: before getProviderByClass");
- cpr = mProviderMap.getProviderByClass(comp, userId);
- checkTime(startTime, "getContentProviderImpl: after getProviderByClass");
- boolean firstClass = cpr == null;
- if (firstClass) {
- final long ident = Binder.clearCallingIdentity();
-
- // If permissions need a review before any of the app components can run,
- // we return no provider and launch a review activity if the calling app
- // is in the foreground.
- if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId)) {
- return null;
- }
-
- try {
- checkTime(startTime, "getContentProviderImpl: before getApplicationInfo");
- ApplicationInfo ai =
- AppGlobals.getPackageManager().
- getApplicationInfo(
- cpi.applicationInfo.packageName,
- STOCK_PM_FLAGS, userId);
- checkTime(startTime, "getContentProviderImpl: after getApplicationInfo");
- if (ai == null) {
- Slog.w(TAG, "No package info for content provider "
- + cpi.name);
- return null;
- }
- ai = getAppInfoForUser(ai, userId);
- cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
- } catch (RemoteException ex) {
- // pm is in same process, this will never happen.
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- } else if (dyingProc == cpr.proc && dyingProc != null) {
- // The old stable connection's client should be killed during proc cleaning up,
- // so do not re-use the old ContentProviderRecord, otherwise the new clients
- // could get killed unexpectedly.
- cpr = new ContentProviderRecord(cpr);
- // This is sort of "firstClass"
- firstClass = true;
- }
-
- checkTime(startTime, "getContentProviderImpl: now have ContentProviderRecord");
-
- if (r != null && cpr.canRunHere(r)) {
- // If this is a multiprocess provider, then just return its
- // info and allow the caller to instantiate it. Only do
- // this if the provider is the same user as the caller's
- // process, or can run as root (so can be in any process).
- return cpr.newHolder(null);
- }
-
- if (DEBUG_PROVIDER) Slog.w(TAG_PROVIDER, "LAUNCHING REMOTE PROVIDER (myuid "
- + (r != null ? r.uid : null) + " pruid " + cpr.appInfo.uid + "): "
- + cpr.info.name + " callers=" + Debug.getCallers(6));
-
- // This is single process, and our app is now connecting to it.
- // See if we are already in the process of launching this
- // provider.
- final int N = mLaunchingProviders.size();
- int i;
- for (i = 0; i < N; i++) {
- if (mLaunchingProviders.get(i) == cpr) {
- break;
- }
- }
-
- // If the provider is not already being launched, then get it
- // started.
- if (i >= N) {
- final long origId = Binder.clearCallingIdentity();
-
- try {
- // Content provider is now in use, its package can't be stopped.
- try {
- checkTime(startTime, "getContentProviderImpl: before set stopped state");
- AppGlobals.getPackageManager().setPackageStoppedState(
- cpr.appInfo.packageName, false, userId);
- checkTime(startTime, "getContentProviderImpl: after set stopped state");
- } catch (RemoteException e) {
- } catch (IllegalArgumentException e) {
- Slog.w(TAG, "Failed trying to unstop package "
- + cpr.appInfo.packageName + ": " + e);
- }
-
- // Use existing process if already started
- checkTime(startTime, "getContentProviderImpl: looking for process record");
- ProcessRecord proc = getProcessRecordLocked(
- cpi.processName, cpr.appInfo.uid, false);
- if (proc != null && proc.thread != null && !proc.killed) {
- if (DEBUG_PROVIDER) Slog.d(TAG_PROVIDER,
- "Installing in existing process " + proc);
- if (!proc.pubProviders.containsKey(cpi.name)) {
- checkTime(startTime, "getContentProviderImpl: scheduling install");
- proc.pubProviders.put(cpi.name, cpr);
- try {
- proc.thread.scheduleInstallProvider(cpi);
- } catch (RemoteException e) {
- }
- }
- } else {
- checkTime(startTime, "getContentProviderImpl: before start process");
- proc = startProcessLocked(cpi.processName,
- cpr.appInfo, false, 0,
- new HostingRecord("content provider",
- new ComponentName(cpi.applicationInfo.packageName,
- cpi.name)),
- ZYGOTE_POLICY_FLAG_EMPTY, false, false, false);
- checkTime(startTime, "getContentProviderImpl: after start process");
- if (proc == null) {
- Slog.w(TAG, "Unable to launch app "
- + cpi.applicationInfo.packageName + "/"
- + cpi.applicationInfo.uid + " for provider "
- + name + ": process is bad");
- return null;
- }
- }
- cpr.launchingApp = proc;
- mLaunchingProviders.add(cpr);
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
- checkTime(startTime, "getContentProviderImpl: updating data structures");
-
- // Make sure the provider is published (the same provider class
- // may be published under multiple names).
- if (firstClass) {
- mProviderMap.putProviderByClass(comp, cpr);
- }
-
- mProviderMap.putProviderByName(name, cpr);
- conn = incProviderCountLocked(r, cpr, token, callingUid,
- callingPackage, callingTag, stable, false, startTime);
- if (conn != null) {
- conn.waiting = true;
- }
- }
- checkTime(startTime, "getContentProviderImpl: done!");
-
- grantImplicitAccess(userId, null /*intent*/, callingUid,
- UserHandle.getAppId(cpi.applicationInfo.uid));
- }
-
- // Wait for the provider to be published...
- final long timeout =
- SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS;
- boolean timedOut = false;
- synchronized (cpr) {
- while (cpr.provider == null) {
- if (cpr.launchingApp == null) {
- Slog.w(TAG, "Unable to launch app "
- + cpi.applicationInfo.packageName + "/"
- + cpi.applicationInfo.uid + " for provider "
- + name + ": launching app became null");
- EventLogTags.writeAmProviderLostProcess(
- UserHandle.getUserId(cpi.applicationInfo.uid),
- cpi.applicationInfo.packageName,
- cpi.applicationInfo.uid, name);
- return null;
- }
- try {
- final long wait = Math.max(0L, timeout - SystemClock.uptimeMillis());
- if (DEBUG_MU) Slog.v(TAG_MU,
- "Waiting to start provider " + cpr
- + " launchingApp=" + cpr.launchingApp + " for " + wait + " ms");
- if (conn != null) {
- conn.waiting = true;
- }
- cpr.wait(wait);
- if (cpr.provider == null) {
- timedOut = true;
- break;
- }
- } catch (InterruptedException ex) {
- } finally {
- if (conn != null) {
- conn.waiting = false;
- }
- }
- }
- }
- if (timedOut) {
- // Note we do it after releasing the lock.
- String callerName = "unknown";
- if (caller != null) {
- synchronized (this) {
- final ProcessRecord record = mProcessList.getLRURecordForAppLocked(caller);
- if (record != null) {
- callerName = record.processName;
- }
- }
- }
-
- Slog.wtf(TAG, "Timeout waiting for provider "
- + cpi.applicationInfo.packageName + "/"
- + cpi.applicationInfo.uid + " for provider "
- + name
- + " providerRunning=" + providerRunning
- + " caller=" + callerName + "/" + Binder.getCallingUid());
- return null;
- }
-
- return cpr.newHolder(conn);
- }
-
- private static final class StartActivityRunnable implements Runnable {
- private final Context mContext;
- private final Intent mIntent;
- private final UserHandle mUserHandle;
-
- StartActivityRunnable(Context context, Intent intent, UserHandle userHandle) {
- this.mContext = context;
- this.mIntent = intent;
- this.mUserHandle = userHandle;
- }
-
- @Override
- public void run() {
- mContext.startActivityAsUser(mIntent, mUserHandle);
- }
- }
-
- private boolean requestTargetProviderPermissionsReviewIfNeededLocked(ProviderInfo cpi,
- ProcessRecord r, final int userId) {
- if (getPackageManagerInternalLocked().isPermissionsReviewRequired(
- cpi.packageName, userId)) {
-
- final boolean callerForeground = r == null || r.setSchedGroup
- != ProcessList.SCHED_GROUP_BACKGROUND;
-
- // Show a permission review UI only for starting from a foreground app
- if (!callerForeground) {
- Slog.w(TAG, "u" + userId + " Instantiating a provider in package"
- + cpi.packageName + " requires a permissions review");
- return false;
- }
-
- final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
- intent.addFlags(FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, cpi.packageName);
-
- if (DEBUG_PERMISSIONS_REVIEW) {
- Slog.i(TAG, "u" + userId + " Launching permission review "
- + "for package " + cpi.packageName);
- }
-
- final UserHandle userHandle = new UserHandle(userId);
- mHandler.post(new StartActivityRunnable(mContext, intent, userHandle));
-
- return false;
- }
-
- return true;
- }
-
/**
* Returns the PackageManager. Used by classes hosted by {@link ActivityManagerService}. The
* PackageManager could be unavailable at construction time and therefore needs to be accessed
@@ -7551,293 +6706,6 @@
}
@Override
- public final ContentProviderHolder getContentProvider(
- IApplicationThread caller, String callingPackage, String name, int userId,
- boolean stable) {
- enforceNotIsolatedCaller("getContentProvider");
- if (caller == null) {
- String msg = "null IApplicationThread when getting content provider "
- + name;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
- // The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
- // with cross-user grant.
- final int callingUid = Binder.getCallingUid();
- if (callingPackage != null && mAppOpsService.checkPackage(callingUid, callingPackage)
- != AppOpsManager.MODE_ALLOWED) {
- throw new SecurityException("Given calling package " + callingPackage
- + " does not match caller's uid " + callingUid);
- }
- return getContentProviderImpl(caller, name, null, callingUid, callingPackage,
- null, stable, userId);
- }
-
- public ContentProviderHolder getContentProviderExternal(
- String name, int userId, IBinder token, String tag) {
- enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
- "Do not have permission in call getContentProviderExternal()");
- userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
- userId, false, ALLOW_FULL_ONLY, "getContentProvider", null);
- return getContentProviderExternalUnchecked(name, token, Binder.getCallingUid(),
- tag != null ? tag : "*external*", userId);
- }
-
- private ContentProviderHolder getContentProviderExternalUnchecked(String name,
- IBinder token, int callingUid, String callingTag, int userId) {
- return getContentProviderImpl(null, name, token, callingUid, null, callingTag,
- true, userId);
- }
-
- /**
- * Drop a content provider from a ProcessRecord's bookkeeping
- */
- public void removeContentProvider(IBinder connection, boolean stable) {
- enforceNotIsolatedCaller("removeContentProvider");
- long ident = Binder.clearCallingIdentity();
- try {
- synchronized (this) {
- ContentProviderConnection conn;
- try {
- conn = (ContentProviderConnection)connection;
- } catch (ClassCastException e) {
- String msg ="removeContentProvider: " + connection
- + " not a ContentProviderConnection";
- Slog.w(TAG, msg);
- throw new IllegalArgumentException(msg);
- }
- if (conn == null) {
- throw new NullPointerException("connection is null");
- }
- if (decProviderCountLocked(conn, null, null, stable)) {
- updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- /** @deprecated - Use {@link #removeContentProviderExternalAsUser} which takes a user ID. */
- @Deprecated
- @Override
- public void removeContentProviderExternal(String name, IBinder token) {
- removeContentProviderExternalAsUser(name, token, UserHandle.getCallingUserId());
- }
-
- @Override
- public void removeContentProviderExternalAsUser(String name, IBinder token, int userId) {
- enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
- "Do not have permission in call removeContentProviderExternal()");
- long ident = Binder.clearCallingIdentity();
- try {
- removeContentProviderExternalUnchecked(name, token, userId);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- private void removeContentProviderExternalUnchecked(String name, IBinder token, int userId) {
- synchronized (this) {
- ContentProviderRecord cpr = mProviderMap.getProviderByName(name, userId);
- if(cpr == null) {
- //remove from mProvidersByClass
- if(DEBUG_ALL) Slog.v(TAG, name+" content provider not found in providers list");
- return;
- }
-
- //update content provider record entry info
- ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
- ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId);
- if (localCpr.hasExternalProcessHandles()) {
- if (localCpr.removeExternalProcessHandleLocked(token)) {
- updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
- } else {
- Slog.e(TAG, "Attmpt to remove content provider " + localCpr
- + " with no external reference for token: "
- + token + ".");
- }
- } else {
- Slog.e(TAG, "Attmpt to remove content provider: " + localCpr
- + " with no external references.");
- }
- }
- }
-
- public final void publishContentProviders(IApplicationThread caller,
- List<ContentProviderHolder> providers) {
- if (providers == null) {
- return;
- }
-
- enforceNotIsolatedCaller("publishContentProviders");
- synchronized (this) {
- final ProcessRecord r = getRecordForAppLocked(caller);
- if (DEBUG_MU) Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid);
- if (r == null) {
- throw new SecurityException(
- "Unable to find app for caller " + caller
- + " (pid=" + Binder.getCallingPid()
- + ") when publishing content providers");
- }
-
- final long origId = Binder.clearCallingIdentity();
-
- final int N = providers.size();
- for (int i = 0; i < N; i++) {
- ContentProviderHolder src = providers.get(i);
- if (src == null || src.info == null || src.provider == null) {
- continue;
- }
- ContentProviderRecord dst = r.pubProviders.get(src.info.name);
- if (DEBUG_MU) Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
- if (dst != null) {
- ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
- mProviderMap.putProviderByClass(comp, dst);
- String names[] = dst.info.authority.split(";");
- for (int j = 0; j < names.length; j++) {
- mProviderMap.putProviderByName(names[j], dst);
- }
-
- int launchingCount = mLaunchingProviders.size();
- int j;
- boolean wasInLaunchingProviders = false;
- for (j = 0; j < launchingCount; j++) {
- if (mLaunchingProviders.get(j) == dst) {
- mLaunchingProviders.remove(j);
- wasInLaunchingProviders = true;
- j--;
- launchingCount--;
- }
- }
- if (wasInLaunchingProviders) {
- mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
- }
- // Make sure the package is associated with the process.
- // XXX We shouldn't need to do this, since we have added the package
- // when we generated the providers in generateApplicationProvidersLocked().
- // But for some reason in some cases we get here with the package no longer
- // added... for now just patch it in to make things happy.
- r.addPackage(dst.info.applicationInfo.packageName,
- dst.info.applicationInfo.longVersionCode, mProcessStats);
- synchronized (dst) {
- dst.provider = src.provider;
- dst.setProcess(r);
- dst.notifyAll();
- }
- dst.mRestartCount = 0;
- updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
- maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
- src.info.authority);
- }
- }
-
- Binder.restoreCallingIdentity(origId);
- }
- }
-
- @Override
- public boolean refContentProvider(IBinder connection, int stable, int unstable) {
- ContentProviderConnection conn;
- try {
- conn = (ContentProviderConnection)connection;
- } catch (ClassCastException e) {
- String msg ="refContentProvider: " + connection
- + " not a ContentProviderConnection";
- Slog.w(TAG, msg);
- throw new IllegalArgumentException(msg);
- }
- if (conn == null) {
- throw new NullPointerException("connection is null");
- }
-
- conn.adjustCounts(stable, unstable);
- return !conn.dead;
- }
-
- public void unstableProviderDied(IBinder connection) {
- ContentProviderConnection conn;
- try {
- conn = (ContentProviderConnection)connection;
- } catch (ClassCastException e) {
- String msg ="refContentProvider: " + connection
- + " not a ContentProviderConnection";
- Slog.w(TAG, msg);
- throw new IllegalArgumentException(msg);
- }
- if (conn == null) {
- throw new NullPointerException("connection is null");
- }
-
- // Safely retrieve the content provider associated with the connection.
- IContentProvider provider;
- synchronized (this) {
- provider = conn.provider.provider;
- }
-
- if (provider == null) {
- // Um, yeah, we're way ahead of you.
- return;
- }
-
- // Make sure the caller is being honest with us.
- if (provider.asBinder().pingBinder()) {
- // Er, no, still looks good to us.
- synchronized (this) {
- Slog.w(TAG, "unstableProviderDied: caller " + Binder.getCallingUid()
- + " says " + conn + " died, but we don't agree");
- return;
- }
- }
-
- // Well look at that! It's dead!
- synchronized (this) {
- if (conn.provider.provider != provider) {
- // But something changed... good enough.
- return;
- }
-
- ProcessRecord proc = conn.provider.proc;
- if (proc == null || proc.thread == null) {
- // Seems like the process is already cleaned up.
- return;
- }
-
- // As far as we're concerned, this is just like receiving a
- // death notification... just a bit prematurely.
- reportUidInfoMessageLocked(TAG,
- "Process " + proc.processName + " (pid " + proc.pid
- + ") early provider death",
- proc.info.uid);
- final long ident = Binder.clearCallingIdentity();
- try {
- appDiedLocked(proc, "unstable content provider");
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
- @Override
- public void appNotRespondingViaProvider(IBinder connection) {
- enforceCallingPermission(REMOVE_TASKS, "appNotRespondingViaProvider()");
-
- final ContentProviderConnection conn = (ContentProviderConnection) connection;
- if (conn == null) {
- Slog.w(TAG, "ContentProviderConnection is null");
- return;
- }
-
- final ProcessRecord host = conn.provider.proc;
- if (host == null) {
- Slog.w(TAG, "Failed to find hosting ProcessRecord");
- return;
- }
-
- mAnrHelper.appNotResponding(host, "ContentProvider not responding");
- }
-
- @Override
public void appNotResponding(final String reason) {
final int callingPid = Binder.getCallingPid();
@@ -7852,43 +6720,6 @@
}
}
- public final void installSystemProviders() {
- List<ProviderInfo> providers;
- synchronized (this) {
- ProcessRecord app = mProcessList.mProcessNames.get("system", SYSTEM_UID);
- providers = generateApplicationProvidersLocked(app);
- if (providers != null) {
- for (int i=providers.size()-1; i>=0; i--) {
- ProviderInfo pi = (ProviderInfo)providers.get(i);
- if ((pi.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {
- Slog.w(TAG, "Not installing system proc provider " + pi.name
- + ": not system .apk");
- providers.remove(i);
- }
- }
- }
- }
- if (providers != null) {
- mSystemThread.installSystemProviders(providers);
- }
-
- synchronized (this) {
- mSystemProvidersInstalled = true;
- }
- mConstants.start(mContext.getContentResolver());
- mCoreSettingsObserver = new CoreSettingsObserver(this);
- mActivityTaskManager.installSystemProviders();
- mDevelopmentSettingsObserver = new DevelopmentSettingsObserver();
- SettingsToPropertiesMapper.start(mContext.getContentResolver());
- mOomAdjuster.initSettings();
-
- // Now that the settings provider is published we can consider sending
- // in a rescue party.
- RescueParty.onSettingsProviderPublished(mContext);
-
- //mUsageStatsService.monitorPackages();
- }
-
void startPersistentApps(int matchFlags) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) return;
@@ -7907,54 +6738,66 @@
}
}
+ // =========================================================
+ // CONTENT PROVIDERS
+ // =========================================================
+
+ public ContentProviderHelper getContentProviderHelper() {
+ return mCpHelper;
+ }
+
+ @Override
+ public final ContentProviderHolder getContentProvider(
+ IApplicationThread caller, String callingPackage, String name, int userId,
+ boolean stable) {
+ return mCpHelper.getContentProvider(caller, callingPackage, name, userId, stable);
+ }
+
+ @Override
+ public ContentProviderHolder getContentProviderExternal(
+ String name, int userId, IBinder token, String tag) {
+ return mCpHelper.getContentProviderExternal(name, userId, token, tag);
+ }
+
/**
- * When a user is unlocked, we need to install encryption-unaware providers
- * belonging to any running apps.
+ * Drop a content provider from a ProcessRecord's bookkeeping
*/
- void installEncryptionUnawareProviders(int userId) {
- // We're only interested in providers that are encryption unaware, and
- // we don't care about uninstalled apps, since there's no way they're
- // running at this point.
- final int matchFlags = GET_PROVIDERS | MATCH_DIRECT_BOOT_UNAWARE;
+ @Override
+ public void removeContentProvider(IBinder connection, boolean stable) {
+ mCpHelper.removeContentProvider(connection, stable);
+ }
- synchronized (this) {
- final int NP = mProcessList.mProcessNames.getMap().size();
- for (int ip = 0; ip < NP; ip++) {
- final SparseArray<ProcessRecord> apps = mProcessList.mProcessNames.getMap().valueAt
- (ip);
- final int NA = apps.size();
- for (int ia = 0; ia < NA; ia++) {
- final ProcessRecord app = apps.valueAt(ia);
- if (app.userId != userId || app.thread == null || app.unlocked) continue;
+ /** @deprecated - Use {@link #removeContentProviderExternalAsUser} which takes a user ID. */
+ @Deprecated
+ @Override
+ public void removeContentProviderExternal(String name, IBinder token) {
+ removeContentProviderExternalAsUser(name, token, UserHandle.getCallingUserId());
+ }
- final int NG = app.pkgList.size();
- for (int ig = 0; ig < NG; ig++) {
- try {
- final String pkgName = app.pkgList.keyAt(ig);
- final PackageInfo pkgInfo = AppGlobals.getPackageManager()
- .getPackageInfo(pkgName, matchFlags, userId);
- if (pkgInfo != null && !ArrayUtils.isEmpty(pkgInfo.providers)) {
- for (ProviderInfo pi : pkgInfo.providers) {
- // TODO: keep in sync with generateApplicationProvidersLocked
- final boolean processMatch = Objects.equals(pi.processName,
- app.processName) || pi.multiprocess;
- final boolean userMatch = isSingleton(pi.processName,
- pi.applicationInfo, pi.name, pi.flags)
- ? (app.userId == UserHandle.USER_SYSTEM) : true;
- if (processMatch && userMatch) {
- Log.v(TAG, "Installing " + pi);
- app.thread.scheduleInstallProvider(pi);
- } else {
- Log.v(TAG, "Skipping " + pi);
- }
- }
- }
- } catch (RemoteException ignored) {
- }
- }
- }
- }
- }
+ @Override
+ public void removeContentProviderExternalAsUser(String name, IBinder token, int userId) {
+ mCpHelper.removeContentProviderExternalAsUser(name, token, userId);
+ }
+
+ @Override
+ public final void publishContentProviders(IApplicationThread caller,
+ List<ContentProviderHolder> providers) {
+ mCpHelper.publishContentProviders(caller, providers);
+ }
+
+ @Override
+ public boolean refContentProvider(IBinder connection, int stable, int unstable) {
+ return mCpHelper.refContentProvider(connection, stable, unstable);
+ }
+
+ @Override
+ public void unstableProviderDied(IBinder connection) {
+ mCpHelper.unstableProviderDied(connection);
+ }
+
+ @Override
+ public void appNotRespondingViaProvider(IBinder connection) {
+ mCpHelper.appNotRespondingViaProvider(connection);
}
/**
@@ -7971,64 +6814,9 @@
* @deprecated -- use getProviderMimeTypeAsync.
*/
@Deprecated
+ @Override
public String getProviderMimeType(Uri uri, int userId) {
- enforceNotIsolatedCaller("getProviderMimeType");
- final String name = uri.getAuthority();
- int callingUid = Binder.getCallingUid();
- int callingPid = Binder.getCallingPid();
- long ident = 0;
- boolean clearedIdentity = false;
- userId = mUserController.unsafeConvertIncomingUser(userId);
- if (canClearIdentity(callingPid, callingUid, userId)) {
- clearedIdentity = true;
- ident = Binder.clearCallingIdentity();
- }
- ContentProviderHolder holder = null;
- try {
- holder = getContentProviderExternalUnchecked(name, null, callingUid,
- "*getmimetype*", userId);
- if (holder != null) {
- final IBinder providerConnection = holder.connection;
- final ComponentName providerName = holder.info.getComponentName();
- // Note: creating a new Runnable instead of using a lambda here since lambdas in
- // java provide no guarantee that there will be a new instance returned every call.
- // Hence, it's possible that a cached copy is returned and the ANR is executed on
- // the incorrect provider.
- final Runnable providerNotResponding = new Runnable() {
- @Override
- public void run() {
- Log.w(TAG, "Provider " + providerName + " didn't return from getType().");
- appNotRespondingViaProvider(providerConnection);
- }
- };
- mHandler.postDelayed(providerNotResponding, 1000);
- try {
- return holder.provider.getType(uri);
- } finally {
- mHandler.removeCallbacks(providerNotResponding);
- }
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Content provider dead retrieving " + uri, e);
- return null;
- } catch (Exception e) {
- Log.w(TAG, "Exception while determining type of " + uri, e);
- return null;
- } finally {
- // We need to clear the identity to call removeContentProviderExternalUnchecked
- if (!clearedIdentity) {
- ident = Binder.clearCallingIdentity();
- }
- try {
- if (holder != null) {
- removeContentProviderExternalUnchecked(name, null, userId);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- return null;
+ return mCpHelper.getProviderMimeType(uri, userId);
}
/**
@@ -8039,82 +6827,7 @@
*/
@Override
public void getProviderMimeTypeAsync(Uri uri, int userId, RemoteCallback resultCallback) {
- enforceNotIsolatedCaller("getProviderMimeTypeAsync");
- final String name = uri.getAuthority();
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int safeUserId = mUserController.unsafeConvertIncomingUser(userId);
- final long ident = canClearIdentity(callingPid, callingUid, userId)
- ? Binder.clearCallingIdentity() : 0;
- try {
- final ContentProviderHolder holder = getContentProviderExternalUnchecked(name, null,
- callingUid, "*getmimetype*", safeUserId);
- if (holder != null) {
- holder.provider.getTypeAsync(uri, new RemoteCallback(result -> {
- final long identity = Binder.clearCallingIdentity();
- try {
- removeContentProviderExternalUnchecked(name, null, safeUserId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- resultCallback.sendResult(result);
- }));
- } else {
- resultCallback.sendResult(Bundle.EMPTY);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Content provider dead retrieving " + uri, e);
- resultCallback.sendResult(Bundle.EMPTY);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- int checkContentProviderUriPermission(Uri uri, int userId, int callingUid, int modeFlags) {
- if (Thread.holdsLock(mActivityTaskManager.getGlobalLock())) {
- Slog.wtf(TAG, new IllegalStateException("Unable to check Uri permission"
- + " because caller is holding WM lock; assuming permission denied"));
- return PackageManager.PERMISSION_DENIED;
- }
-
- final String name = uri.getAuthority();
- final long ident = Binder.clearCallingIdentity();
- ContentProviderHolder holder = null;
- try {
- holder = getContentProviderExternalUnchecked(name, null, callingUid,
- "*checkContentProviderUriPermission*", userId);
- if (holder != null) {
- return holder.provider.checkUriPermission(null, null, uri, callingUid, modeFlags);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Content provider dead retrieving " + uri, e);
- return PackageManager.PERMISSION_DENIED;
- } catch (Exception e) {
- Log.w(TAG, "Exception while determining type of " + uri, e);
- return PackageManager.PERMISSION_DENIED;
- } finally {
- try {
- if (holder != null) {
- removeContentProviderExternalUnchecked(name, null, userId);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- return PackageManager.PERMISSION_DENIED;
- }
-
- private boolean canClearIdentity(int callingPid, int callingUid, int userId) {
- if (UserHandle.getUserId(callingUid) == userId) {
- return true;
- }
- if (checkComponentPermission(INTERACT_ACROSS_USERS, callingPid,
- callingUid, -1, true) == PackageManager.PERMISSION_GRANTED
- || checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid,
- callingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
- return true;
- }
- return false;
+ mCpHelper.getProviderMimeTypeAsync(uri, userId, resultCallback);
}
// =========================================================
@@ -8240,12 +6953,13 @@
mActivityTaskManager.unhandledBack();
}
+ // TODO: Move to ContentProviderHelper?
public ParcelFileDescriptor openContentUri(String uriString) throws RemoteException {
enforceNotIsolatedCaller("openContentUri");
final int userId = UserHandle.getCallingUserId();
final Uri uri = Uri.parse(uriString);
String name = uri.getAuthority();
- ContentProviderHolder cph = getContentProviderExternalUnchecked(name, null,
+ ContentProviderHolder cph = mCpHelper.getContentProviderExternalUnchecked(name, null,
Binder.getCallingUid(), "*opencontent*", userId);
ParcelFileDescriptor pfd = null;
if (cph != null) {
@@ -8267,7 +6981,7 @@
// Ensure that whatever happens, we clean up the identity state
sCallerIdentity.remove();
// Ensure we're done with the provider.
- removeContentProviderExternalUnchecked(name, null, userId);
+ mCpHelper.removeContentProviderExternalUnchecked(name, null, userId);
}
} else {
Slog.d(TAG, "Failed to get provider for authority '" + name + "'");
@@ -10570,7 +9284,7 @@
pw.println("-------------------------------------------------------------------------------");
}
}
- dumpProvidersLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+ mCpHelper.dumpProvidersLocked(fd, pw, args, opti, dumpAll, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
@@ -10762,7 +9476,7 @@
if (args.length > 2) System.arraycopy(args, opti, newArgs, 0,
args.length - opti);
}
- if (!dumpProviderProto(fd, pw, name, newArgs)) {
+ if (!mCpHelper.dumpProviderProto(fd, pw, name, newArgs)) {
pw.println("No providers match: " + name);
pw.println("Use -h for help.");
}
@@ -10901,13 +9615,13 @@
newArgs = new String[args.length - opti];
if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti);
}
- if (!dumpProvider(fd, pw, name, newArgs, 0, dumpAll)) {
+ if (!mCpHelper.dumpProvider(fd, pw, name, newArgs, 0, dumpAll)) {
pw.println("No providers match: " + name);
pw.println("Use -h for help.");
}
} else if ("providers".equals(cmd) || "prov".equals(cmd)) {
synchronized (this) {
- dumpProvidersLocked(fd, pw, args, opti, true, dumpPackage);
+ mCpHelper.dumpProvidersLocked(fd, pw, args, opti, true, dumpPackage);
}
} else if ("service".equals(cmd)) {
String[] newArgs;
@@ -12142,28 +10856,6 @@
reportLmkKillAtOrBelow(pw, ProcessList.FOREGROUND_APP_ADJ);
}
- /**
- * There are three ways to call this:
- * - no provider specified: dump all the providers
- * - a flattened component name that matched an existing provider was specified as the
- * first arg: dump that one provider
- * - the first arg isn't the flattened component name of an existing provider:
- * dump all providers whose component contains the first arg as a substring
- */
- protected boolean dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args,
- int opti, boolean dumpAll) {
- return mProviderMap.dumpProvider(fd, pw, name, args, opti, dumpAll);
- }
-
- /**
- * Similar to the dumpProvider, but only dumps the first matching provider.
- * The provider is responsible for dumping as proto.
- */
- protected boolean dumpProviderProto(FileDescriptor fd, PrintWriter pw, String name,
- String[] args) {
- return mProviderMap.dumpProviderProto(fd, pw, name, args);
- }
-
public static class ItemMatcher {
ArrayList<ComponentName> components;
ArrayList<String> strings;
@@ -12467,43 +11159,6 @@
}
}
- void dumpProvidersLocked(FileDescriptor fd, PrintWriter pw, String[] args,
- int opti, boolean dumpAll, String dumpPackage) {
- boolean needSep;
- boolean printedAnything = false;
-
- ItemMatcher matcher = new ItemMatcher();
- matcher.build(args, opti);
-
- pw.println("ACTIVITY MANAGER CONTENT PROVIDERS (dumpsys activity providers)");
-
- needSep = mProviderMap.dumpProvidersLocked(pw, dumpAll, dumpPackage);
- printedAnything |= needSep;
-
- if (mLaunchingProviders.size() > 0) {
- boolean printed = false;
- for (int i=mLaunchingProviders.size()-1; i>=0; i--) {
- ContentProviderRecord r = mLaunchingProviders.get(i);
- if (dumpPackage != null && !dumpPackage.equals(r.name.getPackageName())) {
- continue;
- }
- if (!printed) {
- if (needSep) pw.println();
- needSep = true;
- pw.println(" Launching content providers:");
- printed = true;
- printedAnything = true;
- }
- pw.print(" Launching #"); pw.print(i); pw.print(": ");
- pw.println(r);
- }
- }
-
- if (!printedAnything) {
- pw.println(" (nothing)");
- }
- }
-
@GuardedBy("this")
void dumpPermissionsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
@@ -14654,89 +13309,6 @@
}
/**
- * Remove the dying provider from known provider map and launching provider map.
- * @param proc The dying process recoder
- * @param cpr The provider to be removed.
- * @param always If true, remove the provider from launching map always, no more restart attempt
- * @return true if the given provider is in launching
- */
- private final boolean removeDyingProviderLocked(ProcessRecord proc,
- ContentProviderRecord cpr, boolean always) {
- boolean inLaunching = mLaunchingProviders.contains(cpr);
- if (inLaunching && !always && ++cpr.mRestartCount > ContentProviderRecord.MAX_RETRY_COUNT) {
- // It's being launched but we've reached maximum attempts, force the removal
- always = true;
- }
-
- if (!inLaunching || always) {
- synchronized (cpr) {
- cpr.launchingApp = null;
- cpr.notifyAll();
- }
- final int userId = UserHandle.getUserId(cpr.uid);
- // Don't remove from provider map if it doesn't match
- // could be a new content provider is starting
- if (mProviderMap.getProviderByClass(cpr.name, userId) == cpr) {
- mProviderMap.removeProviderByClass(cpr.name, userId);
- }
- String names[] = cpr.info.authority.split(";");
- for (int j = 0; j < names.length; j++) {
- // Don't remove from provider map if it doesn't match
- // could be a new content provider is starting
- if (mProviderMap.getProviderByName(names[j], userId) == cpr) {
- mProviderMap.removeProviderByName(names[j], userId);
- }
- }
- }
-
- for (int i = cpr.connections.size() - 1; i >= 0; i--) {
- ContentProviderConnection conn = cpr.connections.get(i);
- if (conn.waiting) {
- // If this connection is waiting for the provider, then we don't
- // need to mess with its process unless we are always removing
- // or for some reason the provider is not currently launching.
- if (inLaunching && !always) {
- continue;
- }
- }
- ProcessRecord capp = conn.client;
- conn.dead = true;
- if (conn.stableCount() > 0) {
- if (!capp.isPersistent() && capp.thread != null
- && capp.pid != 0
- && capp.pid != MY_PID) {
- capp.kill("depends on provider "
- + cpr.name.flattenToShortString()
- + " in dying proc " + (proc != null ? proc.processName : "??")
- + " (adj " + (proc != null ? proc.setAdj : "??") + ")",
- ApplicationExitInfo.REASON_DEPENDENCY_DIED,
- ApplicationExitInfo.SUBREASON_UNKNOWN,
- true);
- }
- } else if (capp.thread != null && conn.provider.provider != null) {
- try {
- capp.thread.unstableProviderDied(conn.provider.provider.asBinder());
- } catch (RemoteException e) {
- }
- // In the protocol here, we don't expect the client to correctly
- // clean up this connection, we'll just remove it.
- cpr.connections.remove(i);
- if (conn.client.conProviders.remove(conn)) {
- stopAssociationLocked(capp.uid, capp.processName, cpr.uid,
- cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
- }
- }
- }
-
- if (inLaunching && always) {
- mLaunchingProviders.remove(cpr);
- cpr.mRestartCount = 0;
- inLaunching = false;
- }
- return inLaunching;
- }
-
- /**
* Main code for cleaning up a process when it has gone away. This is
* called both as a result of the process dying, or directly when stopping
* a process when running in single process mode.
@@ -14786,7 +13358,7 @@
continue;
}
final boolean alwaysRemove = app.bad || !allowRestart;
- final boolean inLaunching = removeDyingProviderLocked(app, cpr, alwaysRemove);
+ final boolean inLaunching = mCpHelper.removeDyingProviderLocked(app, cpr, alwaysRemove);
if (!alwaysRemove && inLaunching && cpr.hasConnectionOrHandle()) {
// We left the provider in the launching list, need to
// restart it.
@@ -14799,7 +13371,7 @@
app.pubProviders.clear();
// Take care of any launching providers waiting for this process.
- if (cleanupAppInLaunchingProvidersLocked(app, false)) {
+ if (mCpHelper.cleanupAppInLaunchingProvidersLocked(app, false)) {
mProcessList.noteProcessDiedLocked(app);
restart = true;
}
@@ -14822,15 +13394,7 @@
// XXX Commented out for now. Trying to figure out a way to reproduce
// the actual situation to identify what is actually going on.
if (false) {
- for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) {
- ContentProviderRecord cpr = mLaunchingProviders.get(i);
- if (cpr.connections.size() <= 0 && !cpr.hasExternalProcessHandles()) {
- synchronized (cpr) {
- cpr.launchingApp = null;
- cpr.notifyAll();
- }
- }
- }
+ mCpHelper.cleanupLaunchingProviders();
}
skipCurrentReceiverLocked(app);
@@ -14950,39 +13514,6 @@
return false;
}
- boolean checkAppInLaunchingProvidersLocked(ProcessRecord app) {
- for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) {
- ContentProviderRecord cpr = mLaunchingProviders.get(i);
- if (cpr.launchingApp == app) {
- return true;
- }
- }
- return false;
- }
-
- boolean cleanupAppInLaunchingProvidersLocked(ProcessRecord app, boolean alwaysBad) {
- // Look through the content providers we are waiting to have launched,
- // and if any run in this process then either schedule a restart of
- // the process or kill the client waiting for it if this process has
- // gone bad.
- boolean restart = false;
- for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) {
- ContentProviderRecord cpr = mLaunchingProviders.get(i);
- if (cpr.launchingApp == app) {
- if (++cpr.mRestartCount > ContentProviderRecord.MAX_RETRY_COUNT) {
- // It's being launched but we've reached maximum attempts, mark it as bad
- alwaysBad = true;
- }
- if (!alwaysBad && !app.bad && cpr.hasConnectionOrHandle()) {
- restart = true;
- } else {
- removeDyingProviderLocked(app, cpr, true);
- }
- }
- }
- return restart;
- }
-
// =========================================================
// SERVICES
// =========================================================
@@ -15688,7 +14219,7 @@
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
null, null, -1, -1, false, null, null, OP_NONE, null, receivers,
- null, 0, null, null, false, true, true, -1, false,
+ null, 0, null, null, false, true, true, -1, false, null,
false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */);
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
@@ -15935,7 +14466,7 @@
resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions,
appOp, bOptions, ordered, sticky, callingPid, callingUid, realCallingUid,
realCallingPid, userId, false /* allowBackgroundActivityStarts */,
- null /*broadcastWhitelist*/);
+ null /* tokenNeededForBackgroundActivityStarts */, null /* broadcastWhitelist */);
}
@GuardedBy("this")
@@ -15945,6 +14476,7 @@
Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
int realCallingPid, int userId, boolean allowBackgroundActivityStarts,
+ @Nullable IBinder backgroundActivityStartsToken,
@Nullable int[] broadcastWhitelist) {
intent = new Intent(intent);
@@ -16035,6 +14567,8 @@
throw new SecurityException(msg);
} else {
allowBackgroundActivityStarts = true;
+ // We set the token to null since if it wasn't for it we'd allow anyway here
+ backgroundActivityStartsToken = null;
}
}
}
@@ -16503,7 +15037,8 @@
callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
resultCode, resultData, resultExtras, ordered, sticky, false, userId,
- allowBackgroundActivityStarts, timeoutExempt);
+ allowBackgroundActivityStarts, backgroundActivityStartsToken,
+ timeoutExempt);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
final boolean replaced = replacePending
&& (queue.replaceParallelBroadcastLocked(r) != null);
@@ -16600,7 +15135,8 @@
callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
resultData, resultExtras, ordered, sticky, false, userId,
- allowBackgroundActivityStarts, timeoutExempt);
+ allowBackgroundActivityStarts, backgroundActivityStartsToken,
+ timeoutExempt);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r);
@@ -16760,7 +15296,8 @@
int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
- int userId, boolean allowBackgroundActivityStarts) {
+ int userId, boolean allowBackgroundActivityStarts,
+ @Nullable IBinder backgroundActivityStartsToken) {
synchronized(this) {
intent = verifyBroadcastLocked(intent);
@@ -16772,6 +15309,7 @@
resultTo, resultCode, resultData, resultExtras, requiredPermissions,
OP_NONE, bOptions, serialized, sticky, -1, uid, realCallingUid,
realCallingPid, userId, allowBackgroundActivityStarts,
+ backgroundActivityStartsToken,
null /*broadcastWhitelist*/);
} finally {
Binder.restoreCallingIdentity(origId);
@@ -17852,25 +16390,6 @@
}
}
- private void maybeUpdateProviderUsageStatsLocked(ProcessRecord app, String providerPkgName,
- String authority) {
- if (app == null) return;
- if (app.getCurProcState() <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
- UserState userState = mUserController.getStartedUserState(app.userId);
- if (userState == null) return;
- final long now = SystemClock.elapsedRealtime();
- Long lastReported = userState.mProviderLastReportedFg.get(authority);
- if (lastReported == null || lastReported < now - 60 * 1000L) {
- if (mSystemReady) {
- // Cannot touch the user stats if not system ready
- mUsageStatsService.reportContentProviderUsage(
- authority, providerPkgName, app.userId);
- }
- userState.mProviderLastReportedFg.put(authority, now);
- }
- }
- }
-
final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
if (proc.thread != null && proc.baseProcessTracker != null) {
final int procState = proc.getReportedProcState();
@@ -18263,6 +16782,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);
@@ -18934,13 +17474,13 @@
public final class LocalService extends ActivityManagerInternal {
@Override
public String checkContentProviderAccess(String authority, int userId) {
- return ActivityManagerService.this.checkContentProviderAccess(authority, userId);
+ return mCpHelper.checkContentProviderAccess(authority, userId);
}
@Override
public int checkContentProviderUriPermission(Uri uri, int userId,
int callingUid, int modeFlags) {
- return ActivityManagerService.this.checkContentProviderUriPermission(uri,
+ return mCpHelper.checkContentProviderUriPermission(uri,
userId, callingUid, modeFlags);
}
@@ -19441,12 +17981,14 @@
int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
- int userId, boolean allowBackgroundActivityStarts) {
+ int userId, boolean allowBackgroundActivityStarts,
+ @Nullable IBinder backgroundActivityStartsToken) {
synchronized (ActivityManagerService.this) {
return ActivityManagerService.this.broadcastIntentInPackage(packageName, featureId,
uid, realCallingUid, realCallingPid, intent, resolvedType, resultTo,
resultCode, resultData, resultExtras, requiredPermission, bOptions,
- serialized, sticky, userId, allowBackgroundActivityStarts);
+ serialized, sticky, userId, allowBackgroundActivityStarts,
+ backgroundActivityStartsToken);
}
}
@@ -19468,6 +18010,7 @@
null /*resultExtras*/, requiredPermissions, AppOpsManager.OP_NONE,
null /*options*/, serialized, false /*sticky*/, callingPid, callingUid,
callingUid, callingPid, userId, false /*allowBackgroundStarts*/,
+ null /*tokenNeededForBackgroundActivityStarts*/,
appIdWhitelist);
} finally {
Binder.restoreCallingIdentity(origId);
@@ -19479,7 +18022,8 @@
@Override
public ComponentName startServiceInPackage(int uid, Intent service, String resolvedType,
boolean fgRequired, String callingPackage, @Nullable String callingFeatureId,
- int userId, boolean allowBackgroundActivityStarts)
+ int userId, boolean allowBackgroundActivityStarts,
+ @Nullable IBinder backgroundActivityStartsToken)
throws TransactionTooLargeException {
synchronized(ActivityManagerService.this) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
@@ -20515,4 +19059,17 @@
Binder.restoreCallingIdentity(token);
}
}
+
+ /**
+ * Resets the state of the {@link com.android.server.am.AppErrors} instance.
+ * This is intended for testing within the CTS only and is protected by
+ * android.permission.RESET_APP_ERRORS.
+ */
+ @Override
+ public void resetAppErrors() {
+ enforceCallingPermission(Manifest.permission.RESET_APP_ERRORS, "resetAppErrors");
+ synchronized (this) {
+ mAppErrors.resetStateLocked();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index a36a18b..2e92ac0 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -107,6 +107,16 @@
mPackageWatchdog = watchdog;
}
+ /** Resets the current state but leaves the constructor-provided fields unchanged. */
+ public void resetStateLocked() {
+ Slog.i(TAG, "Resetting AppErrors");
+ mAppsNotReportingCrashes.clear();
+ mProcessCrashTimes.clear();
+ mProcessCrashTimesPersistent.clear();
+ mProcessCrashShowDialogTimes.clear();
+ mBadProcesses.clear();
+ }
+
void dumpDebug(ProtoOutputStream proto, long fieldId, String dumpPackage) {
if (mProcessCrashTimes.getMap().isEmpty() && mBadProcesses.getMap().isEmpty()) {
return;
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index be17b1b..494f06e 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -62,7 +62,8 @@
public float DEFERRAL_DECAY_FACTOR = DEFAULT_DEFERRAL_DECAY_FACTOR;
// Minimum that the deferral time can decay to until the backlog fully clears
public long DEFERRAL_FLOOR = DEFAULT_DEFERRAL_FLOOR;
- // For how long after a whitelisted receiver's start its process can start a background activity
+ // For a receiver that has been allowed to start background activities, how long after it
+ // started its process can start a background activity.
public long ALLOW_BG_ACTIVITY_START_TIMEOUT = DEFAULT_ALLOW_BG_ACTIVITY_START_TIMEOUT;
// Settings override tracking for this instance
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 1fa62c6..12937b9 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -1677,7 +1677,7 @@
// that request - we don't want the token to be swept from under our feet...
mHandler.removeCallbacksAndMessages(msgToken);
// ...then add the token
- proc.addAllowBackgroundActivityStartsToken(r);
+ proc.addAllowBackgroundActivityStartsToken(r, r.mBackgroundActivityStartsToken);
}
final void setBroadcastTimeoutLocked(long timeoutTime) {
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 8ef67f9..198ba34 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -89,9 +89,12 @@
int manifestSkipCount; // number of manifest receivers skipped.
BroadcastQueue queue; // the outbound queue handling this broadcast
- // if set to true, app's process will be temporarily whitelisted to start activities
- // from background for the duration of the broadcast dispatch
+ // if set to true, app's process will be temporarily allowed to start activities from background
+ // for the duration of the broadcast dispatch
final boolean allowBackgroundActivityStarts;
+ // token used to trace back the grant for activity starts, optional
+ @Nullable
+ final IBinder mBackgroundActivityStartsToken;
static final int IDLE = 0;
static final int APP_RECEIVE = 1;
@@ -240,7 +243,8 @@
String[] _requiredPermissions, int _appOp, BroadcastOptions _options, List _receivers,
IIntentReceiver _resultTo, int _resultCode, String _resultData, Bundle _resultExtras,
boolean _serialized, boolean _sticky, boolean _initialSticky, int _userId,
- boolean _allowBackgroundActivityStarts, boolean _timeoutExempt) {
+ boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken,
+ boolean timeoutExempt) {
if (_intent == null) {
throw new NullPointerException("Can't construct with a null intent");
}
@@ -270,8 +274,9 @@
userId = _userId;
nextReceiver = 0;
state = IDLE;
- allowBackgroundActivityStarts = _allowBackgroundActivityStarts;
- timeoutExempt = _timeoutExempt;
+ this.allowBackgroundActivityStarts = allowBackgroundActivityStarts;
+ mBackgroundActivityStartsToken = backgroundActivityStartsToken;
+ this.timeoutExempt = timeoutExempt;
}
/**
@@ -317,6 +322,7 @@
manifestSkipCount = from.manifestSkipCount;
queue = from.queue;
allowBackgroundActivityStarts = from.allowBackgroundActivityStarts;
+ mBackgroundActivityStartsToken = from.mBackgroundActivityStartsToken;
timeoutExempt = from.timeoutExempt;
}
@@ -352,7 +358,7 @@
callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, appOp, options, splitReceivers, resultTo, resultCode,
resultData, resultExtras, ordered, sticky, initialSticky, userId,
- allowBackgroundActivityStarts, timeoutExempt);
+ allowBackgroundActivityStarts, mBackgroundActivityStartsToken, timeoutExempt);
split.splitToken = this.splitToken;
return split;
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
new file mode 100644
index 0000000..1e0c243
--- /dev/null
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -0,0 +1,1708 @@
+/*
+ * 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.am;
+
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.os.Process.PROC_CHAR;
+import static android.os.Process.PROC_OUT_LONG;
+import static android.os.Process.PROC_PARENS;
+import static android.os.Process.PROC_SPACE_TERM;
+import static android.os.Process.SYSTEM_UID;
+import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
+import static android.os.Process.readProcFile;
+
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
+import static com.android.server.am.ActivityManagerService.TAG_MU;
+
+import android.Manifest;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
+import android.app.ApplicationExitInfo;
+import android.app.ContentProviderHolder;
+import android.app.IApplicationThread;
+import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.IContentProvider;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PathPermission;
+import android.content.pm.ProviderInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.RescueParty;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Activity manager code dealing with content providers.
+ */
+public class ContentProviderHelper {
+ private static final String TAG = "ContentProviderHelper";
+
+ private ActivityManagerService mService;
+
+ private boolean mSystemProvidersInstalled;
+
+ private final ProviderMap mProviderMap;
+
+ /**
+ * List of content providers who have clients waiting for them. The
+ * application is currently being launched and the provider will be
+ * removed from this list once it is published.
+ */
+ private final ArrayList<ContentProviderRecord> mLaunchingProviders = new ArrayList<>();
+
+ ContentProviderHelper(ActivityManagerService service, boolean createProviderMap) {
+ mService = service;
+ mProviderMap = createProviderMap ? new ProviderMap(mService) : null;
+ }
+
+ ProviderMap getProviderMap() {
+ return mProviderMap;
+ }
+
+ List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
+ List<ProviderInfo> providers = null;
+ try {
+ providers = AppGlobals.getPackageManager().queryContentProviders(
+ app.processName, app.uid, ActivityManagerService.STOCK_PM_FLAGS
+ | PackageManager.GET_URI_PERMISSION_PATTERNS
+ | PackageManager.MATCH_DIRECT_BOOT_AUTO, /*metaDataKey=*/ null)
+ .getList();
+ } catch (RemoteException ex) {
+ }
+ if (DEBUG_MU) {
+ Slog.v(TAG_MU, "generateApplicationProvidersLocked, app.info.uid = " + app.uid);
+ }
+ int userId = app.userId;
+ if (providers != null) {
+ int N = providers.size();
+ app.pubProviders.ensureCapacity(N + app.pubProviders.size());
+ for (int i = 0; i < N; i++) {
+ // TODO: keep logic in sync with installEncryptionUnawareProviders
+ ProviderInfo cpi = (ProviderInfo) providers.get(i);
+ boolean singleton = mService.isSingleton(cpi.processName, cpi.applicationInfo,
+ cpi.name, cpi.flags);
+ if (singleton && UserHandle.getUserId(app.uid) != UserHandle.USER_SYSTEM) {
+ // This is a singleton provider, but a user besides the
+ // default user is asking to initialize a process it runs
+ // in... well, no, it doesn't actually run in this process,
+ // it runs in the process of the default user. Get rid of it.
+ providers.remove(i);
+ N--;
+ i--;
+ continue;
+ }
+
+ ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
+ ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
+ if (cpr == null) {
+ cpr = new ContentProviderRecord(mService, cpi, app.info, comp, singleton);
+ mProviderMap.putProviderByClass(comp, cpr);
+ }
+ if (DEBUG_MU) {
+ Slog.v(TAG_MU, "generateApplicationProvidersLocked, cpi.uid = " + cpr.uid);
+ }
+ app.pubProviders.put(cpi.name, cpr);
+ if (!cpi.multiprocess || !"android".equals(cpi.packageName)) {
+ // Don't add this if it is a platform component that is marked
+ // to run in multiple processes, because this is actually
+ // part of the framework so doesn't make sense to track as a
+ // separate apk in the process.
+ app.addPackage(cpi.applicationInfo.packageName,
+ cpi.applicationInfo.longVersionCode, mService.mProcessStats);
+ }
+ mService.notifyPackageUse(cpi.applicationInfo.packageName,
+ PackageManager.NOTIFY_PACKAGE_USE_CONTENT_PROVIDER);
+ }
+ }
+ return providers;
+ }
+
+ @GuardedBy("mService")
+ private ContentProviderConnection incProviderCountLocked(ProcessRecord r,
+ final ContentProviderRecord cpr, IBinder externalProcessToken, int callingUid,
+ String callingPackage, String callingTag, boolean stable, boolean updateLru,
+ long startTime, ProcessList processList) {
+ if (r != null) {
+ for (int i = 0; i < r.conProviders.size(); i++) {
+ ContentProviderConnection conn = r.conProviders.get(i);
+ if (conn.provider == cpr) {
+ conn.incrementCount(stable);
+ return conn;
+ }
+ }
+
+ // Create a new ContentProviderConnection. The reference count
+ // is known to be 1.
+ ContentProviderConnection conn = new ContentProviderConnection(cpr, r, callingPackage);
+ conn.startAssociationIfNeeded();
+ conn.initializeCount(stable);
+ cpr.connections.add(conn);
+ r.conProviders.add(conn);
+ mService.startAssociationLocked(r.uid, r.processName, r.getCurProcState(),
+ cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
+ if (updateLru && cpr.proc != null
+ && r != null && r.setAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
+ // If this is a perceptible app accessing the provider, make
+ // sure to count it as being accessed and thus back up on
+ // the LRU list. This is good because content providers are
+ // often expensive to start. The calls to checkTime() use
+ // the "getContentProviderImpl" tag here, because it's part
+ // of the checktime log in getContentProviderImpl().
+ checkTime(startTime, "getContentProviderImpl: before updateLruProcess");
+ processList.updateLruProcessLocked(cpr.proc, false, null);
+ checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
+ }
+ return conn;
+ }
+ cpr.addExternalProcessHandleLocked(externalProcessToken, callingUid, callingTag);
+ return null;
+ }
+
+ @GuardedBy("mService")
+ private boolean decProviderCountLocked(ContentProviderConnection conn,
+ ContentProviderRecord cpr,
+ IBinder externalProcessToken, boolean stable) {
+ if (conn != null) {
+ cpr = conn.provider;
+ final int referenceCount = conn.decrementCount(stable);
+ if (referenceCount == 0) {
+ conn.stopAssociation();
+ cpr.connections.remove(conn);
+ conn.client.conProviders.remove(conn);
+ if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
+ // The client is more important than last activity -- note the time this
+ // is happening, so we keep the old provider process around a bit as last
+ // activity to avoid thrashing it.
+ if (cpr.proc != null) {
+ cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
+ }
+ }
+ mService.stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
+ cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
+ return true;
+ }
+ return false;
+ }
+ cpr.removeExternalProcessHandleLocked(externalProcessToken);
+ return false;
+ }
+
+ private static final class StartActivityRunnable implements Runnable {
+ private final Context mContext;
+ private final Intent mIntent;
+ private final UserHandle mUserHandle;
+
+ StartActivityRunnable(Context context, Intent intent, UserHandle userHandle) {
+ this.mContext = context;
+ this.mIntent = intent;
+ this.mUserHandle = userHandle;
+ }
+
+ @Override
+ public void run() {
+ mContext.startActivityAsUser(mIntent, mUserHandle);
+ }
+ }
+
+ private boolean requestTargetProviderPermissionsReviewIfNeededLocked(ProviderInfo cpi,
+ ProcessRecord r, final int userId, Context context) {
+ if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
+ cpi.packageName, userId)) {
+
+ final boolean callerForeground = r == null || r.setSchedGroup
+ != ProcessList.SCHED_GROUP_BACKGROUND;
+
+ // Show a permission review UI only for starting from a foreground app
+ if (!callerForeground) {
+ Slog.w(TAG, "u" + userId + " Instantiating a provider in package"
+ + cpi.packageName + " requires a permissions review");
+ return false;
+ }
+
+ final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
+ intent.addFlags(FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, cpi.packageName);
+
+ if (ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW) {
+ Slog.i(TAG, "u" + userId + " Launching permission review "
+ + "for package " + cpi.packageName);
+ }
+
+ final UserHandle userHandle = new UserHandle(userId);
+ mService.mHandler.post(new StartActivityRunnable(context, intent, userHandle));
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if the calling UID has a possible chance at accessing the provider
+ * at the given authority and user.
+ */
+ public String checkContentProviderAccess(String authority, int userId) {
+ if (userId == UserHandle.USER_ALL) {
+ mService.mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL, TAG);
+ userId = UserHandle.getCallingUserId();
+ }
+
+ ProviderInfo cpi = null;
+ try {
+ cpi = AppGlobals.getPackageManager().resolveContentProvider(authority,
+ ActivityManagerService.STOCK_PM_FLAGS
+ | PackageManager.GET_URI_PERMISSION_PATTERNS
+ | PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ userId);
+ } catch (RemoteException ignored) {
+ }
+ if (cpi == null) {
+ return "Failed to find provider " + authority + " for user " + userId
+ + "; expected to find a valid ContentProvider for this authority";
+ }
+
+ ProcessRecord r = null;
+ synchronized (mService.mPidsSelfLocked) {
+ r = mService.mPidsSelfLocked.get(Binder.getCallingPid());
+ }
+ if (r == null) {
+ return "Failed to find PID " + Binder.getCallingPid();
+ }
+
+ synchronized (mService) {
+ return checkContentProviderPermissionLocked(cpi, r, userId, true);
+ }
+ }
+
+ /**
+ * Check if {@link ProcessRecord} has a possible chance at accessing the
+ * given {@link ProviderInfo}. Final permission checking is always done
+ * in {@link ContentProvider}.
+ */
+ private String checkContentProviderPermissionLocked(
+ ProviderInfo cpi, ProcessRecord r, int userId, boolean checkUser) {
+ final int callingPid = (r != null) ? r.pid : Binder.getCallingPid();
+ final int callingUid = (r != null) ? r.uid : Binder.getCallingUid();
+ boolean checkedGrants = false;
+ if (checkUser) {
+ // Looking for cross-user grants before enforcing the typical cross-users permissions
+ int tmpTargetUserId = mService.mUserController.unsafeConvertIncomingUser(userId);
+ if (tmpTargetUserId != UserHandle.getUserId(callingUid)) {
+ if (mService.mUgmInternal.checkAuthorityGrants(
+ callingUid, cpi, tmpTargetUserId, checkUser)) {
+ return null;
+ }
+ checkedGrants = true;
+ }
+ userId = mService.mUserController.handleIncomingUser(callingPid, callingUid, userId,
+ false, ActivityManagerInternal.ALLOW_NON_FULL,
+ "checkContentProviderPermissionLocked " + cpi.authority, null);
+ if (userId != tmpTargetUserId) {
+ // When we actually went to determine the final targer user ID, this ended
+ // up different than our initial check for the authority. This is because
+ // they had asked for USER_CURRENT_OR_SELF and we ended up switching to
+ // SELF. So we need to re-check the grants again.
+ checkedGrants = false;
+ }
+ }
+ if (ActivityManagerService.checkComponentPermission(cpi.readPermission,
+ callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported)
+ == PackageManager.PERMISSION_GRANTED) {
+ return null;
+ }
+ if (ActivityManagerService.checkComponentPermission(cpi.writePermission,
+ callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported)
+ == PackageManager.PERMISSION_GRANTED) {
+ return null;
+ }
+
+ PathPermission[] pps = cpi.pathPermissions;
+ if (pps != null) {
+ int i = pps.length;
+ while (i > 0) {
+ i--;
+ PathPermission pp = pps[i];
+ String pprperm = pp.getReadPermission();
+ if (pprperm != null && ActivityManagerService.checkComponentPermission(pprperm,
+ callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported)
+ == PackageManager.PERMISSION_GRANTED) {
+ return null;
+ }
+ String ppwperm = pp.getWritePermission();
+ if (ppwperm != null && ActivityManagerService.checkComponentPermission(ppwperm,
+ callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported)
+ == PackageManager.PERMISSION_GRANTED) {
+ return null;
+ }
+ }
+ }
+ if (!checkedGrants
+ && mService.mUgmInternal.checkAuthorityGrants(callingUid, cpi, userId, checkUser)) {
+ return null;
+ }
+
+ final String suffix;
+ if (!cpi.exported) {
+ suffix = " that is not exported from UID " + cpi.applicationInfo.uid;
+ } else if (android.Manifest.permission.MANAGE_DOCUMENTS.equals(cpi.readPermission)) {
+ suffix = " requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs";
+ } else {
+ suffix = " requires " + cpi.readPermission + " or " + cpi.writePermission;
+ }
+ final String msg = "Permission Denial: opening provider " + cpi.name
+ + " from " + (r != null ? r : "(null)") + " (pid=" + callingPid
+ + ", uid=" + callingUid + ")" + suffix;
+ Slog.w(TAG, msg);
+ return msg;
+ }
+
+ private String checkContentProviderAssociation(ProcessRecord callingApp, int callingUid,
+ ProviderInfo cpi) {
+ if (callingApp == null) {
+ return mService.validateAssociationAllowedLocked(cpi.packageName,
+ cpi.applicationInfo.uid, null, callingUid) ? null : "<null>";
+ }
+ for (int i = callingApp.pkgList.size() - 1; i >= 0; i--) {
+ if (!mService.validateAssociationAllowedLocked(callingApp.pkgList.keyAt(i),
+ callingApp.uid, cpi.packageName, cpi.applicationInfo.uid)) {
+ return cpi.packageName;
+ }
+ }
+ return null;
+ }
+
+ void removeContentProviderExternalAsUser(String name, IBinder token, int userId) {
+ mService.enforceCallingPermission(
+ android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
+ "Do not have permission in call removeContentProviderExternal()");
+ long ident = Binder.clearCallingIdentity();
+ try {
+ removeContentProviderExternalUnchecked(name, token, userId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ void removeContentProviderExternalUnchecked(String name, IBinder token, int userId) {
+ synchronized (mService) {
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(name, userId);
+ if (cpr == null) {
+ //remove from mProvidersByClass
+ if (ActivityManagerDebugConfig.DEBUG_ALL) {
+ Slog.v(TAG, name + " content provider not found in providers list");
+ }
+ return;
+ }
+
+ // update content provider record entry info
+ ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
+ ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId);
+ if (localCpr.hasExternalProcessHandles()) {
+ if (localCpr.removeExternalProcessHandleLocked(token)) {
+ mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
+ } else {
+ Slog.e(TAG, "Attmpt to remove content provider " + localCpr
+ + " with no external reference for token: "
+ + token + ".");
+ }
+ } else {
+ Slog.e(TAG, "Attmpt to remove content provider: " + localCpr
+ + " with no external references.");
+ }
+ }
+ }
+
+ public boolean refContentProvider(IBinder connection, int stable, int unstable) {
+ ContentProviderConnection conn;
+ try {
+ conn = (ContentProviderConnection) connection;
+ } catch (ClassCastException e) {
+ String msg = "refContentProvider: " + connection + " not a ContentProviderConnection";
+ Slog.w(TAG, msg);
+ throw new IllegalArgumentException(msg);
+ }
+ if (conn == null) {
+ throw new NullPointerException("connection is null");
+ }
+
+ conn.adjustCounts(stable, unstable);
+ return !conn.dead;
+ }
+
+ public void unstableProviderDied(IBinder connection) {
+ ContentProviderConnection conn;
+ try {
+ conn = (ContentProviderConnection) connection;
+ } catch (ClassCastException e) {
+ String msg = "refContentProvider: " + connection + " not a ContentProviderConnection";
+ Slog.w(TAG, msg);
+ throw new IllegalArgumentException(msg);
+ }
+ if (conn == null) {
+ throw new NullPointerException("connection is null");
+ }
+
+ // Safely retrieve the content provider associated with the connection.
+ IContentProvider provider;
+ synchronized (mService) {
+ provider = conn.provider.provider;
+ }
+
+ if (provider == null) {
+ // Um, yeah, we're way ahead of you.
+ return;
+ }
+
+ // Make sure the caller is being honest with us.
+ if (provider.asBinder().pingBinder()) {
+ // Er, no, still looks good to us.
+ synchronized (mService) {
+ Slog.w(TAG, "unstableProviderDied: caller " + Binder.getCallingUid()
+ + " says " + conn + " died, but we don't agree");
+ return;
+ }
+ }
+
+ // Well look at that! It's dead!
+ synchronized (mService) {
+ if (conn.provider.provider != provider) {
+ // But something changed... good enough.
+ return;
+ }
+
+ ProcessRecord proc = conn.provider.proc;
+ if (proc == null || proc.thread == null) {
+ // Seems like the process is already cleaned up.
+ return;
+ }
+
+ // As far as we're concerned, this is just like receiving a
+ // death notification... just a bit prematurely.
+ mService.reportUidInfoMessageLocked(TAG,
+ "Process " + proc.processName + " (pid " + proc.pid
+ + ") early provider death",
+ proc.info.uid);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mService.appDiedLocked(proc, "unstable content provider");
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ public void appNotRespondingViaProvider(IBinder connection) {
+ mService.enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS,
+ "appNotRespondingViaProvider()");
+
+ final ContentProviderConnection conn = (ContentProviderConnection) connection;
+ if (conn == null) {
+ Slog.w(TAG, "ContentProviderConnection is null");
+ return;
+ }
+
+ final ProcessRecord host = conn.provider.proc;
+ if (host == null) {
+ Slog.w(TAG, "Failed to find hosting ProcessRecord");
+ return;
+ }
+
+ mService.mAnrHelper.appNotResponding(host, "ContentProvider not responding");
+ }
+
+ /**
+ * Allows apps to retrieve the MIME type of a URI.
+ * If an app is in the same user as the ContentProvider, or if it is allowed to interact across
+ * users, then it does not need permission to access the ContentProvider.
+ * Either, it needs cross-user uri grants.
+ *
+ * CTS tests for this functionality can be run with "runtest cts-appsecurity".
+ *
+ * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/
+ * src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+ *
+ * @deprecated -- use getProviderMimeTypeAsync.
+ */
+ @Deprecated
+ public String getProviderMimeType(Uri uri, int userId) {
+ mService.enforceNotIsolatedCaller("getProviderMimeType");
+ final String name = uri.getAuthority();
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
+ long ident = 0;
+ boolean clearedIdentity = false;
+ userId = mService.mUserController.unsafeConvertIncomingUser(userId);
+ if (canClearIdentity(callingPid, callingUid, userId)) {
+ clearedIdentity = true;
+ ident = Binder.clearCallingIdentity();
+ }
+ ContentProviderHolder holder = null;
+ try {
+ holder = getContentProviderExternalUnchecked(name, null, callingUid,
+ "*getmimetype*", userId);
+ if (holder != null) {
+ final IBinder providerConnection = holder.connection;
+ final ComponentName providerName = holder.info.getComponentName();
+ // Note: creating a new Runnable instead of using a lambda here since lambdas in
+ // java provide no guarantee that there will be a new instance returned every call.
+ // Hence, it's possible that a cached copy is returned and the ANR is executed on
+ // the incorrect provider.
+ final Runnable providerNotResponding = new Runnable() {
+ @Override
+ public void run() {
+ Log.w(TAG, "Provider " + providerName + " didn't return from getType().");
+ appNotRespondingViaProvider(providerConnection);
+ }
+ };
+ mService.mHandler.postDelayed(providerNotResponding, 1000);
+ try {
+ return holder.provider.getType(uri);
+ } finally {
+ mService.mHandler.removeCallbacks(providerNotResponding);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Content provider dead retrieving " + uri, e);
+ return null;
+ } catch (Exception e) {
+ Log.w(TAG, "Exception while determining type of " + uri, e);
+ return null;
+ } finally {
+ // We need to clear the identity to call removeContentProviderExternalUnchecked
+ if (!clearedIdentity) {
+ ident = Binder.clearCallingIdentity();
+ }
+ try {
+ if (holder != null) {
+ removeContentProviderExternalUnchecked(name, null, userId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Allows apps to retrieve the MIME type of a URI.
+ * If an app is in the same user as the ContentProvider, or if it is allowed to interact across
+ * users, then it does not need permission to access the ContentProvider.
+ * Either way, it needs cross-user uri grants.
+ */
+ public void getProviderMimeTypeAsync(Uri uri, int userId, RemoteCallback resultCallback) {
+ mService.enforceNotIsolatedCaller("getProviderMimeTypeAsync");
+ final String name = uri.getAuthority();
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int safeUserId = mService.mUserController.unsafeConvertIncomingUser(userId);
+ final long ident = canClearIdentity(callingPid, callingUid, userId)
+ ? Binder.clearCallingIdentity() : 0;
+ try {
+ final ContentProviderHolder holder = getContentProviderExternalUnchecked(name, null,
+ callingUid, "*getmimetype*", safeUserId);
+ if (holder != null) {
+ holder.provider.getTypeAsync(uri, new RemoteCallback(result -> {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ removeContentProviderExternalUnchecked(name, null, safeUserId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ resultCallback.sendResult(result);
+ }));
+ } else {
+ resultCallback.sendResult(Bundle.EMPTY);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Content provider dead retrieving " + uri, e);
+ resultCallback.sendResult(Bundle.EMPTY);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private boolean canClearIdentity(int callingPid, int callingUid, int userId) {
+ if (UserHandle.getUserId(callingUid) == userId) {
+ return true;
+ }
+ if (ActivityManagerService.checkComponentPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS, callingPid,
+ callingUid, -1, true) == PackageManager.PERMISSION_GRANTED
+ || ActivityManagerService.checkComponentPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingPid,
+ callingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+ return false;
+ }
+
+ int checkContentProviderUriPermission(Uri uri, int userId, int callingUid, int modeFlags) {
+ if (Thread.holdsLock(mService.mActivityTaskManager.getGlobalLock())) {
+ Slog.wtf(TAG, new IllegalStateException("Unable to check Uri permission"
+ + " because caller is holding WM lock; assuming permission denied"));
+ return PackageManager.PERMISSION_DENIED;
+ }
+
+ final String name = uri.getAuthority();
+ final long ident = Binder.clearCallingIdentity();
+ ContentProviderHolder holder = null;
+ try {
+ holder = getContentProviderExternalUnchecked(name, null, callingUid,
+ "*checkContentProviderUriPermission*", userId);
+ if (holder != null) {
+ return holder.provider.checkUriPermission(null, null, uri, callingUid, modeFlags);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Content provider dead retrieving " + uri, e);
+ return PackageManager.PERMISSION_DENIED;
+ } catch (Exception e) {
+ Log.w(TAG, "Exception while determining type of " + uri, e);
+ return PackageManager.PERMISSION_DENIED;
+ } finally {
+ try {
+ if (holder != null) {
+ removeContentProviderExternalUnchecked(name, null, userId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ return PackageManager.PERMISSION_DENIED;
+ }
+
+ /**
+ * Drop a content provider from a ProcessRecord's bookkeeping
+ */
+ void removeContentProvider(IBinder connection, boolean stable) {
+ mService.enforceNotIsolatedCaller("removeContentProvider");
+ long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mService) {
+ ContentProviderConnection conn;
+ try {
+ conn = (ContentProviderConnection) connection;
+ } catch (ClassCastException e) {
+ String msg = "removeContentProvider: " + connection
+ + " not a ContentProviderConnection";
+ Slog.w(TAG, msg);
+ throw new IllegalArgumentException(msg);
+ }
+ if (conn == null) {
+ throw new NullPointerException("connection is null");
+ }
+ if (decProviderCountLocked(conn, null, null, stable)) {
+ mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ ContentProviderHolder getContentProvider(IApplicationThread caller, String callingPackage,
+ String name, int userId, boolean stable) {
+ mService.enforceNotIsolatedCaller("getContentProvider");
+ if (caller == null) {
+ String msg = "null IApplicationThread when getting content provider " + name;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ // The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
+ // with cross-user grant.
+ final int callingUid = Binder.getCallingUid();
+ if (callingPackage != null && mService.mAppOpsService.checkPackage(
+ callingUid, callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ throw new SecurityException("Given calling package " + callingPackage
+ + " does not match caller's uid " + callingUid);
+ }
+ return getContentProviderImpl(caller, name, null, callingUid, callingPackage,
+ null, stable, userId);
+ }
+
+ private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
+ String name, IBinder token, int callingUid, String callingPackage, String callingTag,
+ boolean stable, int userId) {
+ ContentProviderRecord cpr;
+ ContentProviderConnection conn = null;
+ ProviderInfo cpi = null;
+ boolean providerRunning = false;
+ synchronized (mService) {
+ long startTime = SystemClock.uptimeMillis();
+
+ ProcessRecord r = null;
+ if (caller != null) {
+ r = mService.getRecordForAppLocked(caller);
+ if (r == null) {
+ throw new SecurityException("Unable to find app for caller " + caller
+ + " (pid=" + Binder.getCallingPid() + ") when getting content provider "
+ + name);
+ }
+ }
+
+ boolean checkCrossUser = true;
+
+ checkTime(startTime, "getContentProviderImpl: getProviderByName");
+
+ // First check if this content provider has been published...
+ cpr = mProviderMap.getProviderByName(name, userId);
+ // If that didn't work, check if it exists for user 0 and then
+ // verify that it's a singleton provider before using it.
+ if (cpr == null && userId != UserHandle.USER_SYSTEM) {
+ cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);
+ if (cpr != null) {
+ cpi = cpr.info;
+ if (mService.isSingleton(cpi.processName, cpi.applicationInfo,
+ cpi.name, cpi.flags)
+ && mService.isValidSingletonCall(r == null ? callingUid : r.uid,
+ cpi.applicationInfo.uid)) {
+ userId = UserHandle.USER_SYSTEM;
+ checkCrossUser = false;
+ } else {
+ cpr = null;
+ cpi = null;
+ }
+ }
+ }
+
+ ProcessRecord dyingProc = null;
+ if (cpr != null && cpr.proc != null) {
+ providerRunning = !cpr.proc.killed;
+
+ // Note if killedByAm is also set, this means the provider process has just been
+ // killed by AM (in ProcessRecord.kill()), but appDiedLocked() hasn't been called
+ // yet. So we need to call appDiedLocked() here and let it clean up.
+ // (See the commit message on I2c4ba1e87c2d47f2013befff10c49b3dc337a9a7 to see
+ // how to test this case.)
+ if (cpr.proc.killed && cpr.proc.killedByAm) {
+ Slog.wtf(TAG, cpr.proc.toString() + " was killed by AM but isn't really dead");
+ // Now we are going to wait for the death before starting the new process.
+ dyingProc = cpr.proc;
+ }
+ }
+
+ if (providerRunning) {
+ cpi = cpr.info;
+ String msg;
+
+ if (r != null && cpr.canRunHere(r)) {
+ if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) {
+ throw new SecurityException("Content provider lookup "
+ + cpr.name.flattenToShortString()
+ + " failed: association not allowed with package " + msg);
+ }
+ checkTime(startTime,
+ "getContentProviderImpl: before checkContentProviderPermission");
+ if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
+ != null) {
+ throw new SecurityException(msg);
+ }
+ checkTime(startTime,
+ "getContentProviderImpl: after checkContentProviderPermission");
+
+ // This provider has been published or is in the process
+ // of being published... but it is also allowed to run
+ // in the caller's process, so don't make a connection
+ // and just let the caller instantiate its own instance.
+ ContentProviderHolder holder = cpr.newHolder(null);
+ // don't give caller the provider object, it needs
+ // to make its own.
+ holder.provider = null;
+ return holder; //what?
+ }
+
+ // Don't expose providers between normal apps and instant apps
+ try {
+ if (AppGlobals.getPackageManager()
+ .resolveContentProvider(name, 0 /*flags*/, userId) == null) {
+ return null;
+ }
+ } catch (RemoteException e) {
+ }
+
+ if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) {
+ throw new SecurityException("Content provider lookup "
+ + cpr.name.flattenToShortString()
+ + " failed: association not allowed with package " + msg);
+ }
+ checkTime(startTime,
+ "getContentProviderImpl: before checkContentProviderPermission");
+ if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
+ != null) {
+ throw new SecurityException(msg);
+ }
+ checkTime(startTime,
+ "getContentProviderImpl: after checkContentProviderPermission");
+
+ final long origId = Binder.clearCallingIdentity();
+
+ checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");
+
+ // In this case the provider instance already exists, so we can
+ // return it right away.
+ conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
+ stable, true, startTime, mService.mProcessList);
+
+ checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
+ final int verifiedAdj = cpr.proc.verifiedAdj;
+ boolean success = mService.updateOomAdjLocked(cpr.proc, true,
+ OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
+ // XXX things have changed so updateOomAdjLocked doesn't actually tell us
+ // if the process has been successfully adjusted. So to reduce races with
+ // it, we will check whether the process still exists. Note that this doesn't
+ // completely get rid of races with LMK killing the process, but should make
+ // them much smaller.
+ if (success && verifiedAdj != cpr.proc.setAdj && !isProcessAliveLocked(cpr.proc)) {
+ success = false;
+ }
+ maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
+ checkTime(startTime, "getContentProviderImpl: after updateOomAdj");
+ if (ActivityManagerDebugConfig.DEBUG_PROVIDER) {
+ Slog.i(TAG, "Adjust success: " + success);
+ }
+ // NOTE: there is still a race here where a signal could be
+ // pending on the process even though we managed to update its
+ // adj level. Not sure what to do about this, but at least
+ // the race is now smaller.
+ if (!success) {
+ // Uh oh... it looks like the provider's process
+ // has been killed on us. We need to wait for a new
+ // process to be started, and make sure its death
+ // doesn't kill our process.
+ Slog.wtf(TAG, "Existing provider " + cpr.name.flattenToShortString()
+ + " is crashing; detaching " + r);
+ boolean lastRef = decProviderCountLocked(conn, cpr,
+ token, stable);
+ if (!lastRef) {
+ // This wasn't the last ref our process had on
+ // the provider... we will be killed during cleaning up, bail.
+ return null;
+ }
+ // We'll just start a new process to host the content provider
+ providerRunning = false;
+ conn = null;
+ dyingProc = cpr.proc;
+ } else {
+ cpr.proc.verifiedAdj = cpr.proc.setAdj;
+ }
+
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ if (!providerRunning) {
+ try {
+ checkTime(startTime, "getContentProviderImpl: before resolveContentProvider");
+ cpi = AppGlobals.getPackageManager().resolveContentProvider(name,
+ ActivityManagerService.STOCK_PM_FLAGS
+ | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
+ checkTime(startTime, "getContentProviderImpl: after resolveContentProvider");
+ } catch (RemoteException ex) {
+ }
+ if (cpi == null) {
+ return null;
+ }
+ // If the provider is a singleton AND
+ // (it's a call within the same user || the provider is a
+ // privileged app)
+ // Then allow connecting to the singleton provider
+ boolean singleton = mService.isSingleton(cpi.processName, cpi.applicationInfo,
+ cpi.name, cpi.flags)
+ && mService.isValidSingletonCall(r == null ? callingUid : r.uid,
+ cpi.applicationInfo.uid);
+ if (singleton) {
+ userId = UserHandle.USER_SYSTEM;
+ }
+ cpi.applicationInfo = mService.getAppInfoForUser(cpi.applicationInfo, userId);
+ checkTime(startTime, "getContentProviderImpl: got app info for user");
+
+ String msg;
+ if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) {
+ throw new SecurityException("Content provider lookup " + name
+ + " failed: association not allowed with package " + msg);
+ }
+ checkTime(startTime,
+ "getContentProviderImpl: before checkContentProviderPermission");
+ if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton))
+ != null) {
+ throw new SecurityException(msg);
+ }
+ checkTime(startTime,
+ "getContentProviderImpl: after checkContentProviderPermission");
+
+ if (!mService.mProcessesReady
+ && !cpi.processName.equals("system")) {
+ // If this content provider does not run in the system
+ // process, and the system is not yet ready to run other
+ // processes, then fail fast instead of hanging.
+ throw new IllegalArgumentException(
+ "Attempt to launch content provider before system ready");
+ }
+
+ // If system providers are not installed yet we aggressively crash to avoid
+ // creating multiple instance of these providers and then bad things happen!
+ if (!mSystemProvidersInstalled && cpi.applicationInfo.isSystemApp()
+ && "system".equals(cpi.processName)) {
+ throw new IllegalStateException("Cannot access system provider: '"
+ + cpi.authority + "' before system providers are installed!");
+ }
+
+ // Make sure that the user who owns this provider is running. If not,
+ // we don't want to allow it to run.
+ if (!mService.mUserController.isUserRunning(userId, 0)) {
+ Slog.w(TAG, "Unable to launch app "
+ + cpi.applicationInfo.packageName + "/"
+ + cpi.applicationInfo.uid + " for provider "
+ + name + ": user " + userId + " is stopped");
+ return null;
+ }
+
+ ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
+ checkTime(startTime, "getContentProviderImpl: before getProviderByClass");
+ cpr = mProviderMap.getProviderByClass(comp, userId);
+ checkTime(startTime, "getContentProviderImpl: after getProviderByClass");
+ boolean firstClass = cpr == null;
+ if (firstClass) {
+ final long ident = Binder.clearCallingIdentity();
+
+ // If permissions need a review before any of the app components can run,
+ // we return no provider and launch a review activity if the calling app
+ // is in the foreground.
+ if (!requestTargetProviderPermissionsReviewIfNeededLocked(
+ cpi, r, userId, mService.mContext)) {
+ return null;
+ }
+
+ try {
+ checkTime(startTime, "getContentProviderImpl: before getApplicationInfo");
+ ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
+ cpi.applicationInfo.packageName,
+ ActivityManagerService.STOCK_PM_FLAGS, userId);
+ checkTime(startTime, "getContentProviderImpl: after getApplicationInfo");
+ if (ai == null) {
+ Slog.w(TAG, "No package info for content provider " + cpi.name);
+ return null;
+ }
+ ai = mService.getAppInfoForUser(ai, userId);
+ cpr = new ContentProviderRecord(mService, cpi, ai, comp, singleton);
+ } catch (RemoteException ex) {
+ // pm is in same process, this will never happen.
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ } else if (dyingProc == cpr.proc && dyingProc != null) {
+ // The old stable connection's client should be killed during proc cleaning up,
+ // so do not re-use the old ContentProviderRecord, otherwise the new clients
+ // could get killed unexpectedly.
+ cpr = new ContentProviderRecord(cpr);
+ // This is sort of "firstClass"
+ firstClass = true;
+ }
+
+ checkTime(startTime, "getContentProviderImpl: now have ContentProviderRecord");
+
+ if (r != null && cpr.canRunHere(r)) {
+ // If this is a multiprocess provider, then just return its
+ // info and allow the caller to instantiate it. Only do
+ // this if the provider is the same user as the caller's
+ // process, or can run as root (so can be in any process).
+ return cpr.newHolder(null);
+ }
+
+ if (ActivityManagerDebugConfig.DEBUG_PROVIDER) {
+ Slog.w(TAG, "LAUNCHING REMOTE PROVIDER (myuid " + (r != null ? r.uid : null)
+ + " pruid " + cpr.appInfo.uid + "): " + cpr.info.name
+ + " callers=" + Debug.getCallers(6));
+ }
+
+ // This is single process, and our app is now connecting to it.
+ // See if we are already in the process of launching this
+ // provider.
+ final int N = mLaunchingProviders.size();
+ int i;
+ for (i = 0; i < N; i++) {
+ if (mLaunchingProviders.get(i) == cpr) {
+ break;
+ }
+ }
+
+ // If the provider is not already being launched, then get it
+ // started.
+ if (i >= N) {
+ final long origId = Binder.clearCallingIdentity();
+
+ try {
+ // Content provider is now in use, its package can't be stopped.
+ try {
+ checkTime(startTime,
+ "getContentProviderImpl: before set stopped state");
+ AppGlobals.getPackageManager().setPackageStoppedState(
+ cpr.appInfo.packageName, false, userId);
+ checkTime(startTime, "getContentProviderImpl: after set stopped state");
+ } catch (RemoteException e) {
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Failed trying to unstop package "
+ + cpr.appInfo.packageName + ": " + e);
+ }
+
+ // Use existing process if already started
+ checkTime(startTime, "getContentProviderImpl: looking for process record");
+ ProcessRecord proc = mService.getProcessRecordLocked(
+ cpi.processName, cpr.appInfo.uid, false);
+ if (proc != null && proc.thread != null && !proc.killed) {
+ if (ActivityManagerDebugConfig.DEBUG_PROVIDER) {
+ Slog.d(TAG, "Installing in existing process " + proc);
+ }
+ if (!proc.pubProviders.containsKey(cpi.name)) {
+ checkTime(startTime, "getContentProviderImpl: scheduling install");
+ proc.pubProviders.put(cpi.name, cpr);
+ try {
+ proc.thread.scheduleInstallProvider(cpi);
+ } catch (RemoteException e) {
+ }
+ }
+ } else {
+ checkTime(startTime, "getContentProviderImpl: before start process");
+ proc = mService.startProcessLocked(cpi.processName,
+ cpr.appInfo, false, 0,
+ new HostingRecord("content provider",
+ new ComponentName(cpi.applicationInfo.packageName,
+ cpi.name)),
+ ZYGOTE_POLICY_FLAG_EMPTY, false, false, false);
+ checkTime(startTime, "getContentProviderImpl: after start process");
+ if (proc == null) {
+ Slog.w(TAG, "Unable to launch app "
+ + cpi.applicationInfo.packageName + "/"
+ + cpi.applicationInfo.uid + " for provider "
+ + name + ": process is bad");
+ return null;
+ }
+ }
+ cpr.launchingApp = proc;
+ mLaunchingProviders.add(cpr);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ checkTime(startTime, "getContentProviderImpl: updating data structures");
+
+ // Make sure the provider is published (the same provider class
+ // may be published under multiple names).
+ if (firstClass) {
+ mProviderMap.putProviderByClass(comp, cpr);
+ }
+
+ mProviderMap.putProviderByName(name, cpr);
+ conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
+ stable, false, startTime, mService.mProcessList);
+ if (conn != null) {
+ conn.waiting = true;
+ }
+ }
+ checkTime(startTime, "getContentProviderImpl: done!");
+
+ mService.grantImplicitAccess(userId, null /*intent*/, callingUid,
+ UserHandle.getAppId(cpi.applicationInfo.uid));
+ }
+
+ // Wait for the provider to be published...
+ final long timeout =
+ SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS;
+ boolean timedOut = false;
+ synchronized (cpr) {
+ while (cpr.provider == null) {
+ if (cpr.launchingApp == null) {
+ Slog.w(TAG, "Unable to launch app "
+ + cpi.applicationInfo.packageName + "/"
+ + cpi.applicationInfo.uid + " for provider "
+ + name + ": launching app became null");
+ EventLogTags.writeAmProviderLostProcess(
+ UserHandle.getUserId(cpi.applicationInfo.uid),
+ cpi.applicationInfo.packageName,
+ cpi.applicationInfo.uid, name);
+ return null;
+ }
+ try {
+ final long wait = Math.max(0L, timeout - SystemClock.uptimeMillis());
+ if (DEBUG_MU) {
+ Slog.v(TAG_MU, "Waiting to start provider " + cpr
+ + " launchingApp=" + cpr.launchingApp + " for " + wait + " ms");
+ }
+ if (conn != null) {
+ conn.waiting = true;
+ }
+ cpr.wait(wait);
+ if (cpr.provider == null) {
+ timedOut = true;
+ break;
+ }
+ } catch (InterruptedException ex) {
+ } finally {
+ if (conn != null) {
+ conn.waiting = false;
+ }
+ }
+ }
+ }
+ if (timedOut) {
+ // Note we do it after releasing the lock.
+ String callerName = "unknown";
+ if (caller != null) {
+ synchronized (mService) {
+ final ProcessRecord record =
+ mService.mProcessList.getLRURecordForAppLocked(caller);
+ if (record != null) {
+ callerName = record.processName;
+ }
+ }
+ }
+
+ Slog.wtf(TAG, "Timeout waiting for provider "
+ + cpi.applicationInfo.packageName + "/"
+ + cpi.applicationInfo.uid + " for provider " + name
+ + " providerRunning=" + providerRunning
+ + " caller=" + callerName + "/" + Binder.getCallingUid());
+ return null;
+ }
+ return cpr.newHolder(conn);
+ }
+
+ ContentProviderHolder getContentProviderExternal(
+ String name, int userId, IBinder token, String tag) {
+ mService.enforceCallingPermission(
+ android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
+ "Do not have permission in call getContentProviderExternal()");
+ userId = mService.mUserController.handleIncomingUser(
+ Binder.getCallingPid(), Binder.getCallingUid(), userId,
+ false, ActivityManagerInternal.ALLOW_FULL_ONLY, "getContentProvider", null);
+ return getContentProviderExternalUnchecked(name, token, Binder.getCallingUid(),
+ tag != null ? tag : "*external*", userId);
+ }
+
+ ContentProviderHolder getContentProviderExternalUnchecked(String name,
+ IBinder token, int callingUid, String callingTag, int userId) {
+ return getContentProviderImpl(null, name, token, callingUid, null, callingTag,
+ true, userId);
+ }
+
+ private static final int[] PROCESS_STATE_STATS_FORMAT = new int[] {
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM | PROC_PARENS,
+ PROC_SPACE_TERM | PROC_CHAR | PROC_OUT_LONG, // 3: process state
+ };
+
+ private final long[] mProcessStateStatsLongs = new long[1];
+
+ private boolean isProcessAliveLocked(ProcessRecord proc) {
+ if (proc.pid <= 0) {
+ if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
+ Slog.d(ActivityManagerService.TAG, "Process hasn't started yet: " + proc);
+ }
+ return false;
+ }
+ if (proc.procStatFile == null) {
+ proc.procStatFile = "/proc/" + proc.pid + "/stat";
+ }
+ mProcessStateStatsLongs[0] = 0;
+ if (!readProcFile(proc.procStatFile, PROCESS_STATE_STATS_FORMAT, null,
+ mProcessStateStatsLongs, null)) {
+ if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
+ Slog.d(ActivityManagerService.TAG,
+ "UNABLE TO RETRIEVE STATE FOR " + proc.procStatFile);
+ }
+ return false;
+ }
+ final long state = mProcessStateStatsLongs[0];
+ if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
+ Slog.d(ActivityManagerService.TAG,
+ "RETRIEVED STATE FOR " + proc.procStatFile + ": " + (char) state);
+ }
+ if (state != 'Z' && state != 'X' && state != 'x' && state != 'K') {
+ return Process.getUidForPid(proc.pid) == proc.uid;
+ }
+ return false;
+ }
+
+ public final void publishContentProviders(IApplicationThread caller,
+ List<ContentProviderHolder> providers) {
+ if (providers == null) {
+ return;
+ }
+
+ mService.enforceNotIsolatedCaller("publishContentProviders");
+ synchronized (mService) {
+ final ProcessRecord r = mService.getRecordForAppLocked(caller);
+ if (DEBUG_MU) {
+ Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid);
+ }
+ if (r == null) {
+ throw new SecurityException("Unable to find app for caller " + caller
+ + " (pid=" + Binder.getCallingPid()
+ + ") when publishing content providers");
+ }
+
+ final long origId = Binder.clearCallingIdentity();
+
+ final int N = providers.size();
+ for (int i = 0; i < N; i++) {
+ ContentProviderHolder src = providers.get(i);
+ if (src == null || src.info == null || src.provider == null) {
+ continue;
+ }
+ ContentProviderRecord dst = r.pubProviders.get(src.info.name);
+ if (DEBUG_MU) {
+ Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
+ }
+ if (dst != null) {
+ ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
+ mProviderMap.putProviderByClass(comp, dst);
+ String[] names = dst.info.authority.split(";");
+ for (int j = 0; j < names.length; j++) {
+ mProviderMap.putProviderByName(names[j], dst);
+ }
+
+ int launchingCount = mLaunchingProviders.size();
+ int j;
+ boolean wasInLaunchingProviders = false;
+ for (j = 0; j < launchingCount; j++) {
+ if (mLaunchingProviders.get(j) == dst) {
+ mLaunchingProviders.remove(j);
+ wasInLaunchingProviders = true;
+ j--;
+ launchingCount--;
+ }
+ }
+ if (wasInLaunchingProviders) {
+ mService.mHandler.removeMessages(
+ ActivityManagerService.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
+ }
+ // Make sure the package is associated with the process.
+ // XXX We shouldn't need to do this, since we have added the package
+ // when we generated the providers in generateApplicationProvidersLocked().
+ // But for some reason in some cases we get here with the package no longer
+ // added... for now just patch it in to make things happy.
+ r.addPackage(dst.info.applicationInfo.packageName,
+ dst.info.applicationInfo.longVersionCode, mService.mProcessStats);
+ synchronized (dst) {
+ dst.provider = src.provider;
+ dst.setProcess(r);
+ dst.notifyAll();
+ }
+ dst.mRestartCount = 0;
+ mService.updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
+ maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
+ src.info.authority);
+ }
+ }
+
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ private void maybeUpdateProviderUsageStatsLocked(ProcessRecord app, String providerPkgName,
+ String authority) {
+ if (app == null) return;
+ if (app.getCurProcState() <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+ UserState userState = mService.mUserController.getStartedUserState(app.userId);
+ if (userState == null) return;
+ final long now = SystemClock.elapsedRealtime();
+ Long lastReported = userState.mProviderLastReportedFg.get(authority);
+ if (lastReported == null || lastReported < now - 60 * 1000L) {
+ if (mService.mSystemReady) {
+ // Cannot touch the user stats if not system ready
+ mService.mUsageStatsService.reportContentProviderUsage(
+ authority, providerPkgName, app.userId);
+ }
+ userState.mProviderLastReportedFg.put(authority, now);
+ }
+ }
+ }
+
+ private final class DevelopmentSettingsObserver extends ContentObserver {
+ private final Uri mUri = Settings.Global.getUriFor(
+ Settings.Global.DEVELOPMENT_SETTINGS_ENABLED);
+
+ private final ComponentName mBugreportStorageProvider = new ComponentName(
+ "com.android.shell", "com.android.shell.BugreportStorageProvider");
+
+ DevelopmentSettingsObserver() {
+ super(mService.mHandler);
+ mService.mContext.getContentResolver().registerContentObserver(mUri, false, this,
+ UserHandle.USER_ALL);
+ // Always kick once to ensure that we match current state
+ onChange();
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
+ if (mUri.equals(uri)) {
+ onChange();
+ }
+ }
+
+ private void onChange() {
+ final boolean enabled = Settings.Global.getInt(mService.mContext.getContentResolver(),
+ Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, Build.IS_ENG ? 1 : 0) != 0;
+ mService.mContext.getPackageManager().setComponentEnabledSetting(
+ mBugreportStorageProvider,
+ enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+ : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+ 0);
+ }
+ }
+
+ public final void installSystemProviders() {
+ List<ProviderInfo> providers;
+ synchronized (mService) {
+ ProcessRecord app = mService.mProcessList.mProcessNames.get("system", SYSTEM_UID);
+ providers = generateApplicationProvidersLocked(app);
+ if (providers != null) {
+ for (int i = providers.size() - 1; i >= 0; i--) {
+ ProviderInfo pi = providers.get(i);
+ if ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ Slog.w(TAG, "Not installing system proc provider " + pi.name
+ + ": not system .apk");
+ providers.remove(i);
+ }
+ }
+ }
+ }
+ if (providers != null) {
+ mService.mSystemThread.installSystemProviders(providers);
+ }
+
+ synchronized (mService) {
+ mSystemProvidersInstalled = true;
+ }
+ mService.mConstants.start(mService.mContext.getContentResolver());
+ mService.mCoreSettingsObserver = new CoreSettingsObserver(mService);
+ mService.mActivityTaskManager.installSystemProviders();
+ new DevelopmentSettingsObserver(); // init to observe developer settings enable/disable
+ SettingsToPropertiesMapper.start(mService.mContext.getContentResolver());
+ mService.mOomAdjuster.initSettings();
+
+ // Now that the settings provider is published we can consider sending
+ // in a rescue party.
+ RescueParty.onSettingsProviderPublished(mService.mContext);
+
+ //mUsageStatsService.monitorPackages();
+ }
+
+ /**
+ * When a user is unlocked, we need to install encryption-unaware providers
+ * belonging to any running apps.
+ */
+ void installEncryptionUnawareProviders(int userId) {
+ // We're only interested in providers that are encryption unaware, and
+ // we don't care about uninstalled apps, since there's no way they're
+ // running at this point.
+ final int matchFlags =
+ PackageManager.GET_PROVIDERS | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+
+ synchronized (mService) {
+ final int NP = mService.mProcessList.mProcessNames.getMap().size();
+ for (int ip = 0; ip < NP; ip++) {
+ final SparseArray<ProcessRecord> apps =
+ mService.mProcessList.mProcessNames.getMap().valueAt(ip);
+ final int NA = apps.size();
+ for (int ia = 0; ia < NA; ia++) {
+ final ProcessRecord app = apps.valueAt(ia);
+ if (app.userId != userId || app.thread == null || app.unlocked) continue;
+
+ final int NG = app.pkgList.size();
+ for (int ig = 0; ig < NG; ig++) {
+ try {
+ final String pkgName = app.pkgList.keyAt(ig);
+ final PackageInfo pkgInfo = AppGlobals.getPackageManager()
+ .getPackageInfo(pkgName, matchFlags, userId);
+ if (pkgInfo != null && !ArrayUtils.isEmpty(pkgInfo.providers)) {
+ for (ProviderInfo pi : pkgInfo.providers) {
+ // TODO: keep in sync with generateApplicationProvidersLocked
+ final boolean processMatch = Objects.equals(pi.processName,
+ app.processName) || pi.multiprocess;
+ final boolean userMatch = mService.isSingleton(pi.processName,
+ pi.applicationInfo, pi.name, pi.flags)
+ ? (app.userId == UserHandle.USER_SYSTEM) : true;
+ if (processMatch && userMatch) {
+ Log.v(TAG, "Installing " + pi);
+ app.thread.scheduleInstallProvider(pi);
+ } else {
+ Log.v(TAG, "Skipping " + pi);
+ }
+ }
+ }
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove the dying provider from known provider map and launching provider map.
+ * @param proc The dying process recoder
+ * @param cpr The provider to be removed.
+ * @param always If true, remove the provider from launching map always, no more restart attempt
+ * @return true if the given provider is in launching
+ */
+ boolean removeDyingProviderLocked(ProcessRecord proc,
+ ContentProviderRecord cpr, boolean always) {
+ boolean inLaunching = mLaunchingProviders.contains(cpr);
+ if (inLaunching && !always && ++cpr.mRestartCount > ContentProviderRecord.MAX_RETRY_COUNT) {
+ // It's being launched but we've reached maximum attempts, force the removal
+ always = true;
+ }
+
+ if (!inLaunching || always) {
+ synchronized (cpr) {
+ cpr.launchingApp = null;
+ cpr.notifyAll();
+ }
+ final int userId = UserHandle.getUserId(cpr.uid);
+ // Don't remove from provider map if it doesn't match
+ // could be a new content provider is starting
+ if (mProviderMap.getProviderByClass(cpr.name, userId) == cpr) {
+ mProviderMap.removeProviderByClass(cpr.name, userId);
+ }
+ String[] names = cpr.info.authority.split(";");
+ for (int j = 0; j < names.length; j++) {
+ // Don't remove from provider map if it doesn't match
+ // could be a new content provider is starting
+ if (mProviderMap.getProviderByName(names[j], userId) == cpr) {
+ mProviderMap.removeProviderByName(names[j], userId);
+ }
+ }
+ }
+
+ for (int i = cpr.connections.size() - 1; i >= 0; i--) {
+ ContentProviderConnection conn = cpr.connections.get(i);
+ if (conn.waiting) {
+ // If this connection is waiting for the provider, then we don't
+ // need to mess with its process unless we are always removing
+ // or for some reason the provider is not currently launching.
+ if (inLaunching && !always) {
+ continue;
+ }
+ }
+ ProcessRecord capp = conn.client;
+ conn.dead = true;
+ if (conn.stableCount() > 0) {
+ if (!capp.isPersistent() && capp.thread != null
+ && capp.pid != 0
+ && capp.pid != ActivityManagerService.MY_PID) {
+ capp.kill("depends on provider "
+ + cpr.name.flattenToShortString()
+ + " in dying proc " + (proc != null ? proc.processName : "??")
+ + " (adj " + (proc != null ? proc.setAdj : "??") + ")",
+ ApplicationExitInfo.REASON_DEPENDENCY_DIED,
+ ApplicationExitInfo.SUBREASON_UNKNOWN,
+ true);
+ }
+ } else if (capp.thread != null && conn.provider.provider != null) {
+ try {
+ capp.thread.unstableProviderDied(conn.provider.provider.asBinder());
+ } catch (RemoteException e) {
+ }
+ // In the protocol here, we don't expect the client to correctly
+ // clean up this connection, we'll just remove it.
+ cpr.connections.remove(i);
+ if (conn.client.conProviders.remove(conn)) {
+ mService.stopAssociationLocked(capp.uid, capp.processName, cpr.uid,
+ cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
+ }
+ }
+ }
+
+ if (inLaunching && always) {
+ mLaunchingProviders.remove(cpr);
+ cpr.mRestartCount = 0;
+ inLaunching = false;
+ }
+ return inLaunching;
+ }
+
+ boolean checkAppInLaunchingProvidersLocked(ProcessRecord app) {
+ for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) {
+ ContentProviderRecord cpr = mLaunchingProviders.get(i);
+ if (cpr.launchingApp == app) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean cleanupAppInLaunchingProvidersLocked(ProcessRecord app, boolean alwaysBad) {
+ // Look through the content providers we are waiting to have launched,
+ // and if any run in this process then either schedule a restart of
+ // the process or kill the client waiting for it if this process has
+ // gone bad.
+ boolean restart = false;
+ for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) {
+ ContentProviderRecord cpr = mLaunchingProviders.get(i);
+ if (cpr.launchingApp == app) {
+ if (++cpr.mRestartCount > ContentProviderRecord.MAX_RETRY_COUNT) {
+ // It's being launched but we've reached maximum attempts, mark it as bad
+ alwaysBad = true;
+ }
+ if (!alwaysBad && !app.bad && cpr.hasConnectionOrHandle()) {
+ restart = true;
+ } else {
+ removeDyingProviderLocked(app, cpr, true);
+ }
+ }
+ }
+ return restart;
+ }
+
+ void cleanupLaunchingProviders() {
+ for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) {
+ ContentProviderRecord cpr = mLaunchingProviders.get(i);
+ if (cpr.connections.size() <= 0 && !cpr.hasExternalProcessHandles()) {
+ synchronized (cpr) {
+ cpr.launchingApp = null;
+ cpr.notifyAll();
+ }
+ }
+ }
+ }
+
+ ProviderInfo getProviderInfoLocked(String authority, @UserIdInt int userId,
+ int pmFlags) {
+ ProviderInfo pi = null;
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userId);
+ if (cpr != null) {
+ pi = cpr.info;
+ } else {
+ try {
+ pi = AppGlobals.getPackageManager().resolveContentProvider(
+ authority, PackageManager.GET_URI_PERMISSION_PATTERNS | pmFlags, userId);
+ } catch (RemoteException ex) {
+ }
+ }
+ return pi;
+ }
+
+ @GuardedBy("mService")
+ void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
+ cleanupAppInLaunchingProvidersLocked(app, true);
+ mService.mProcessList.removeProcessLocked(app, false, true,
+ ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
+ ApplicationExitInfo.SUBREASON_UNKNOWN,
+ "timeout publishing content providers");
+ }
+
+ private void checkTime(long startTime, String where) {
+ long now = SystemClock.uptimeMillis();
+ if ((now - startTime) > 50) {
+ // If we are taking more than 50ms, log about it.
+ Slog.w(TAG, "Slow operation: " + (now - startTime) + "ms so far, now at " + where);
+ }
+ }
+
+ void dumpProvidersLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ int opti, boolean dumpAll, String dumpPackage) {
+ boolean needSep;
+ boolean printedAnything = false;
+
+ ActivityManagerService.ItemMatcher matcher = new ActivityManagerService.ItemMatcher();
+ matcher.build(args, opti);
+
+ pw.println("ACTIVITY MANAGER CONTENT PROVIDERS (dumpsys activity providers)");
+
+ needSep = mProviderMap.dumpProvidersLocked(pw, dumpAll, dumpPackage);
+ printedAnything |= needSep;
+
+ if (mLaunchingProviders.size() > 0) {
+ boolean printed = false;
+ for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) {
+ ContentProviderRecord r = mLaunchingProviders.get(i);
+ if (dumpPackage != null && !dumpPackage.equals(r.name.getPackageName())) {
+ continue;
+ }
+ if (!printed) {
+ if (needSep) pw.println();
+ needSep = true;
+ pw.println(" Launching content providers:");
+ printed = true;
+ printedAnything = true;
+ }
+ pw.print(" Launching #"); pw.print(i); pw.print(": ");
+ pw.println(r);
+ }
+ }
+
+ if (!printedAnything) {
+ pw.println(" (nothing)");
+ }
+ }
+
+ /**
+ * There are three ways to call this:
+ * - no provider specified: dump all the providers
+ * - a flattened component name that matched an existing provider was specified as the
+ * first arg: dump that one provider
+ * - the first arg isn't the flattened component name of an existing provider:
+ * dump all providers whose component contains the first arg as a substring
+ */
+ protected boolean dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args,
+ int opti, boolean dumpAll) {
+ return mProviderMap.dumpProvider(fd, pw, name, args, opti, dumpAll);
+ }
+
+ /**
+ * Similar to the dumpProvider, but only dumps the first matching provider.
+ * The provider is responsible for dumping as proto.
+ */
+ protected boolean dumpProviderProto(FileDescriptor fd, PrintWriter pw, String name,
+ String[] args) {
+ return mProviderMap.dumpProviderProto(fd, pw, name, args);
+ }
+}
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 1f826b5..60530c4 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -31,3 +31,5 @@
per-file SettingsToPropertiesMapper.java = omakoto@google.com, svetoslavganov@google.com, yamasani@google.com
per-file CarUserSwitchingDialog.java = keunyoung@google.com, felipeal@google.com, gurunagarajan@google.com
+
+per-file ContentProviderHelper.java = varunshah@google.com, omakoto@google.com, jsharkey@google.com, yamasani@google.com
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 1997dbd..fbfed34 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -437,15 +437,17 @@
break;
case ActivityManager.INTENT_SENDER_BROADCAST:
try {
+ final boolean allowedByToken =
+ mAllowBgActivityStartsForBroadcastSender.contains(whitelistToken);
+ final IBinder bgStartsToken = (allowedByToken) ? whitelistToken : null;
+
// If a completion callback has been requested, require
// that the broadcast be delivered synchronously
int sent = controller.mAmInternal.broadcastIntentInPackage(key.packageName,
key.featureId, uid, callingUid, callingPid, finalIntent,
resolvedType, finishedReceiver, code, null, null,
requiredPermission, options, (finishedReceiver != null), false,
- userId,
- mAllowBgActivityStartsForBroadcastSender.contains(whitelistToken)
- || allowTrampoline);
+ userId, allowedByToken || allowTrampoline, bgStartsToken);
if (sent == ActivityManager.BROADCAST_SUCCESS) {
sendFinish = false;
}
@@ -456,11 +458,14 @@
case ActivityManager.INTENT_SENDER_SERVICE:
case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE:
try {
+ final boolean allowedByToken =
+ mAllowBgActivityStartsForServiceSender.contains(whitelistToken);
+ final IBinder bgStartsToken = (allowedByToken) ? whitelistToken : null;
+
controller.mAmInternal.startServiceInPackage(uid, finalIntent, resolvedType,
key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE,
key.packageName, key.featureId, userId,
- mAllowBgActivityStartsForServiceSender.contains(whitelistToken)
- || allowTrampoline);
+ allowedByToken || allowTrampoline, bgStartsToken);
} catch (RuntimeException e) {
Slog.w(TAG, "Unable to send startService intent", e);
} catch (TransactionTooLargeException e) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index b6ad1a5..5721fb7 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) {
@@ -2931,25 +2951,26 @@
if ((expecting == null) || (old == expecting)) {
mProcessNames.remove(name, uid);
}
- if (old != null && old.uidRecord != null) {
- old.uidRecord.numProcs--;
- old.uidRecord.procRecords.remove(old);
- if (old.uidRecord.numProcs == 0) {
+ final ProcessRecord record = expecting != null ? expecting : old;
+ if (record != null && record.uidRecord != null) {
+ final UidRecord uidRecord = record.uidRecord;
+ uidRecord.numProcs--;
+ uidRecord.procRecords.remove(record);
+ if (uidRecord.numProcs == 0) {
// No more processes using this uid, tell clients it is gone.
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "No more processes in " + old.uidRecord);
- mService.enqueueUidChangeLocked(old.uidRecord, -1, UidRecord.CHANGE_GONE);
+ "No more processes in " + uidRecord);
+ mService.enqueueUidChangeLocked(uidRecord, -1, UidRecord.CHANGE_GONE);
EventLogTags.writeAmUidStopped(uid);
mActiveUids.remove(uid);
mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT,
ActivityManager.PROCESS_CAPABILITY_NONE);
}
- old.uidRecord = null;
+ record.uidRecord = null;
}
mIsolatedProcesses.remove(uid);
mGlobalIsolatedUids.freeIsolatedUidLocked(uid);
// Remove the (expected) ProcessRecord from the app zygote
- final ProcessRecord record = expecting != null ? expecting : old;
if (record != null && record.appZygote) {
removeProcessFromAppZygoteLocked(record);
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index c5152c0..cd4302b 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -25,6 +25,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.MY_PID;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ApplicationErrorReport;
import android.app.ApplicationExitInfo;
@@ -274,9 +275,6 @@
final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();
// All ContentProviderRecord process is using
final ArrayList<ContentProviderConnection> conProviders = new ArrayList<>();
- // A set of tokens that currently contribute to this process being temporarily whitelisted
- // to start activities even if it's not in the foreground
- final ArraySet<Binder> mAllowBackgroundActivityStartsTokens = new ArraySet<>();
// a set of UIDs of all bound clients
private ArraySet<Integer> mBoundClientUids = new ArraySet<>();
@@ -352,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;
@@ -625,13 +625,6 @@
pw.print(prefix); pw.print(" - "); pw.println(receivers.valueAt(i));
}
}
- if (mAllowBackgroundActivityStartsTokens.size() > 0) {
- pw.print(prefix); pw.println("Background activity start whitelist tokens:");
- for (int i = 0; i < mAllowBackgroundActivityStartsTokens.size(); i++) {
- pw.print(prefix); pw.print(" - ");
- pw.println(mAllowBackgroundActivityStartsTokens.valueAt(i));
- }
- }
}
ProcessRecord(ActivityManagerService _service, ApplicationInfo _info, String _processName,
@@ -925,6 +918,7 @@
if (!mPersistent) {
killed = true;
killedByAm = true;
+ mKillTime = SystemClock.uptimeMillis();
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -1328,17 +1322,23 @@
return mUsingWrapper;
}
- void addAllowBackgroundActivityStartsToken(Binder entity) {
+ /**
+ * Allows background activity starts using token {@param entity}. Optionally, you can provide
+ * {@param originatingToken} if you have one such originating token, this is useful for tracing
+ * back the grant in the case of the notification token.
+ */
+ void addAllowBackgroundActivityStartsToken(Binder entity, @Nullable IBinder originatingToken) {
if (entity == null) return;
- mAllowBackgroundActivityStartsTokens.add(entity);
- mWindowProcessController.setAllowBackgroundActivityStarts(true);
+ mWindowProcessController.addAllowBackgroundActivityStartsToken(entity, originatingToken);
}
void removeAllowBackgroundActivityStartsToken(Binder entity) {
if (entity == null) return;
- mAllowBackgroundActivityStartsTokens.remove(entity);
- mWindowProcessController.setAllowBackgroundActivityStarts(
- !mAllowBackgroundActivityStartsTokens.isEmpty());
+ mWindowProcessController.removeAllowBackgroundActivityStartsToken(entity);
+ }
+
+ boolean areBackgroundActivityStartsAllowedByToken() {
+ return mWindowProcessController.areBackgroundActivityStartsAllowedByToken();
}
void addBoundClientUid(int clientUid) {
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 828ac71..022b04d 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -131,13 +131,13 @@
int pendingConnectionImportance; // To be filled in to ProcessRecord once it connects
// any current binding to this service has BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS flag?
- private boolean mHasBindingWhitelistingBgActivityStarts;
- // is this service currently whitelisted to start activities from background by providing
+ private boolean mIsAllowedBgActivityStartsByBinding;
+ // is this service currently allowed to start activities from background by providing
// allowBackgroundActivityStarts=true to startServiceLocked()?
- private boolean mHasStartedWhitelistingBgActivityStarts;
- // used to clean up the state of hasStartedWhitelistingBgActivityStarts after a timeout
- private Runnable mStartedWhitelistingBgActivityStartsCleanUp;
- private ProcessRecord mAppForStartedWhitelistingBgActivityStarts;
+ private boolean mIsAllowedBgActivityStartsByStart;
+ // used to clean up the state of mIsAllowedBgActivityStartsByStart after a timeout
+ private Runnable mCleanUpAllowBgActivityStartsByStartCallback;
+ private ProcessRecord mAppForAllowingBgActivityStartsByStart;
// allow while-in-use permissions in foreground service or not.
// while-in-use permissions in FGS started from background might be restricted.
@@ -396,13 +396,13 @@
if (whitelistManager) {
pw.print(prefix); pw.print("whitelistManager="); pw.println(whitelistManager);
}
- if (mHasBindingWhitelistingBgActivityStarts) {
- pw.print(prefix); pw.print("hasBindingWhitelistingBgActivityStarts=");
- pw.println(mHasBindingWhitelistingBgActivityStarts);
+ if (mIsAllowedBgActivityStartsByBinding) {
+ pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByBinding=");
+ pw.println(mIsAllowedBgActivityStartsByBinding);
}
- if (mHasStartedWhitelistingBgActivityStarts) {
- pw.print(prefix); pw.print("hasStartedWhitelistingBgActivityStarts=");
- pw.println(mHasStartedWhitelistingBgActivityStarts);
+ if (mIsAllowedBgActivityStartsByStart) {
+ pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByStart=");
+ pw.println(mIsAllowedBgActivityStartsByStart);
}
pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs=");
pw.println(mAllowWhileInUsePermissionInFgs);
@@ -560,31 +560,31 @@
public void setProcess(ProcessRecord _proc) {
if (_proc != null) {
- // We're starting a new process for this service, but a previous one is whitelisted.
- // Remove that whitelisting now (unless the new process is the same as the previous one,
- // which is a common case).
- if (mAppForStartedWhitelistingBgActivityStarts != null) {
- if (mAppForStartedWhitelistingBgActivityStarts != _proc) {
- mAppForStartedWhitelistingBgActivityStarts
+ // We're starting a new process for this service, but a previous one is allowed to start
+ // background activities. Remove that ability now (unless the new process is the same as
+ // the previous one, which is a common case).
+ if (mAppForAllowingBgActivityStartsByStart != null) {
+ if (mAppForAllowingBgActivityStartsByStart != _proc) {
+ mAppForAllowingBgActivityStartsByStart
.removeAllowBackgroundActivityStartsToken(this);
- ams.mHandler.removeCallbacks(mStartedWhitelistingBgActivityStartsCleanUp);
+ ams.mHandler.removeCallbacks(mCleanUpAllowBgActivityStartsByStartCallback);
}
}
// Make sure the cleanup callback knows about the new process.
- mAppForStartedWhitelistingBgActivityStarts = mHasStartedWhitelistingBgActivityStarts
+ mAppForAllowingBgActivityStartsByStart = mIsAllowedBgActivityStartsByStart
? _proc : null;
- if (mHasStartedWhitelistingBgActivityStarts
- || mHasBindingWhitelistingBgActivityStarts) {
- _proc.addAllowBackgroundActivityStartsToken(this);
+ if (mIsAllowedBgActivityStartsByStart
+ || mIsAllowedBgActivityStartsByBinding) {
+ _proc.addAllowBackgroundActivityStartsToken(this, null);
} else {
_proc.removeAllowBackgroundActivityStartsToken(this);
}
}
if (app != null && app != _proc) {
- // If the old app is whitelisted because of a service start, leave it whitelisted until
- // the cleanup callback runs. Otherwise we can remove it from the whitelist immediately
- // (it can't be bound now).
- if (!mHasStartedWhitelistingBgActivityStarts) {
+ // If the old app is allowed to start bg activities because of a service start, leave it
+ // that way until the cleanup callback runs. Otherwise we can remove its bg activity
+ // start ability immediately (it can't be bound now).
+ if (!mIsAllowedBgActivityStartsByStart) {
app.removeAllowBackgroundActivityStartsToken(this);
}
app.updateBoundClientUids();
@@ -648,92 +648,93 @@
return startRequested && (stopIfKilled || isStartCanceled) && pendingStarts.isEmpty();
}
- void updateHasBindingWhitelistingBgActivityStarts() {
- boolean hasWhitelistingBinding = false;
+ void updateIsAllowedBgActivityStartsByBinding() {
+ boolean isAllowedByBinding = false;
for (int conni = connections.size() - 1; conni >= 0; conni--) {
ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
for (int i = 0; i < cr.size(); i++) {
if ((cr.get(i).flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
- hasWhitelistingBinding = true;
+ isAllowedByBinding = true;
break;
}
}
- if (hasWhitelistingBinding) {
+ if (isAllowedByBinding) {
break;
}
}
- setHasBindingWhitelistingBgActivityStarts(hasWhitelistingBinding);
+ setAllowedBgActivityStartsByBinding(isAllowedByBinding);
}
- void setHasBindingWhitelistingBgActivityStarts(boolean newValue) {
- if (mHasBindingWhitelistingBgActivityStarts != newValue) {
- mHasBindingWhitelistingBgActivityStarts = newValue;
- updateParentProcessBgActivityStartsWhitelistingToken();
+ void setAllowedBgActivityStartsByBinding(boolean newValue) {
+ if (mIsAllowedBgActivityStartsByBinding != newValue) {
+ mIsAllowedBgActivityStartsByBinding = newValue;
+ updateParentProcessBgActivityStartsToken();
}
}
/**
- * Called when the service is started with allowBackgroundActivityStarts set. We whitelist
- * it for background activity starts, setting up a callback to remove the whitelisting after a
- * timeout. Note that the whitelisting persists for the process even if the service is
- * subsequently stopped.
+ * Called when the service is started with allowBackgroundActivityStarts set. We allow
+ * it for background activity starts, setting up a callback to remove this ability after a
+ * timeout. Note that the ability for starting background activities persists for the process
+ * even if the service is subsequently stopped.
*/
- void whitelistBgActivityStartsOnServiceStart() {
- setHasStartedWhitelistingBgActivityStarts(true);
+ void allowBgActivityStartsOnServiceStart() {
+ setAllowedBgActivityStartsByStart(true);
if (app != null) {
- mAppForStartedWhitelistingBgActivityStarts = app;
+ mAppForAllowingBgActivityStartsByStart = app;
}
// This callback is stateless, so we create it once when we first need it.
- if (mStartedWhitelistingBgActivityStartsCleanUp == null) {
- mStartedWhitelistingBgActivityStartsCleanUp = () -> {
+ if (mCleanUpAllowBgActivityStartsByStartCallback == null) {
+ mCleanUpAllowBgActivityStartsByStartCallback = () -> {
synchronized (ams) {
- if (app == mAppForStartedWhitelistingBgActivityStarts) {
- // The process we whitelisted is still running the service. We remove
- // the started whitelisting, but it may still be whitelisted via bound
- // connections.
- setHasStartedWhitelistingBgActivityStarts(false);
- } else if (mAppForStartedWhitelistingBgActivityStarts != null) {
- // The process we whitelisted is not running the service. It therefore
- // can't be bound so we can unconditionally remove the whitelist.
- mAppForStartedWhitelistingBgActivityStarts
+ if (app == mAppForAllowingBgActivityStartsByStart) {
+ // The process we allowed is still running the service. We remove
+ // the ability by start, but it may still be allowed via bound connections.
+ setAllowedBgActivityStartsByStart(false);
+ } else if (mAppForAllowingBgActivityStartsByStart != null) {
+ // The process we allowed is not running the service. It therefore can't be
+ // bound so we can unconditionally remove the ability.
+ mAppForAllowingBgActivityStartsByStart
.removeAllowBackgroundActivityStartsToken(ServiceRecord.this);
}
- mAppForStartedWhitelistingBgActivityStarts = null;
+ mAppForAllowingBgActivityStartsByStart = null;
}
};
}
// if there's a request pending from the past, drop it before scheduling a new one
- ams.mHandler.removeCallbacks(mStartedWhitelistingBgActivityStartsCleanUp);
- ams.mHandler.postDelayed(mStartedWhitelistingBgActivityStartsCleanUp,
+ ams.mHandler.removeCallbacks(mCleanUpAllowBgActivityStartsByStartCallback);
+ ams.mHandler.postDelayed(mCleanUpAllowBgActivityStartsByStartCallback,
ams.mConstants.SERVICE_BG_ACTIVITY_START_TIMEOUT);
}
- private void setHasStartedWhitelistingBgActivityStarts(boolean newValue) {
- if (mHasStartedWhitelistingBgActivityStarts != newValue) {
- mHasStartedWhitelistingBgActivityStarts = newValue;
- updateParentProcessBgActivityStartsWhitelistingToken();
+ private void setAllowedBgActivityStartsByStart(boolean newValue) {
+ if (mIsAllowedBgActivityStartsByStart != newValue) {
+ mIsAllowedBgActivityStartsByStart = newValue;
+ updateParentProcessBgActivityStartsToken();
}
}
/**
- * Whether the process this service runs in should be temporarily whitelisted to start
+ * Whether the process this service runs in should be temporarily allowed to start
* activities from background depends on the current state of both
- * {@code hasStartedWhitelistingBgActivityStarts} and
- * {@code hasBindingWhitelistingBgActivityStarts}. If either is true, this ServiceRecord
+ * {@code mIsAllowedBgActivityStartsByStart} and
+ * {@code mIsAllowedBgActivityStartsByBinding}. If either is true, this ServiceRecord
* should be contributing as a token in parent ProcessRecord.
*
- * @see com.android.server.am.ProcessRecord#mAllowBackgroundActivityStartsTokens
+ * @see com.android.server.am.ProcessRecord#addAllowBackgroundActivityStartsToken(Binder,
+ * IBinder)
+ * @see com.android.server.am.ProcessRecord#removeAllowBackgroundActivityStartsToken(Binder)
*/
- private void updateParentProcessBgActivityStartsWhitelistingToken() {
+ private void updateParentProcessBgActivityStartsToken() {
if (app == null) {
return;
}
- if (mHasStartedWhitelistingBgActivityStarts || mHasBindingWhitelistingBgActivityStarts) {
+ if (mIsAllowedBgActivityStartsByStart || mIsAllowedBgActivityStartsByBinding) {
// if the token is already there it's safe to "re-add it" - we're dealing with
// a set of Binder objects
- app.addAllowBackgroundActivityStartsToken(this);
+ app.addAllowBackgroundActivityStartsToken(this, null);
} else {
app.removeAllowBackgroundActivityStartsToken(this);
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 9562158..0658e81 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;
@@ -2883,7 +2882,7 @@
}
void installEncryptionUnawareProviders(@UserIdInt int userId) {
- mService.installEncryptionUnawareProviders(userId);
+ mService.mCpHelper.installEncryptionUnawareProviders(userId);
}
void showUserSwitchingDialog(UserInfo fromUser, UserInfo toUser,
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index b323190..e6480fc 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2059,6 +2059,8 @@
public void getHistoricalOps(int uid, String packageName, String attributionTag,
List<String> opNames, int filter, long beginTimeMillis, long endTimeMillis,
int flags, RemoteCallback callback) {
+ PackageManager pm = mContext.getPackageManager();
+
ensureHistoricalOpRequestIsValid(uid, packageName, attributionTag, opNames, filter,
beginTimeMillis, endTimeMillis, flags);
Objects.requireNonNull(callback, "callback cannot be null");
@@ -2066,8 +2068,16 @@
ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
boolean isCallerInstrumented = ami.isUidCurrentlyInstrumented(Binder.getCallingUid());
boolean isCallerSystem = Binder.getCallingPid() == Process.myPid();
+ boolean isCallerPermissionController;
+ try {
+ isCallerPermissionController = pm.getPackageUid(
+ mContext.getPackageManager().getPermissionControllerPackageName(), 0)
+ == Binder.getCallingUid();
+ } catch (PackageManager.NameNotFoundException doesNotHappen) {
+ return;
+ }
- if (!isCallerSystem && !isCallerInstrumented) {
+ if (!isCallerSystem && !isCallerInstrumented && !isCallerPermissionController) {
mHandler.post(() -> callback.sendResult(new Bundle()));
return;
}
@@ -6020,7 +6030,7 @@
private void resampleAppOpForPackageLocked(@NonNull String packageName, boolean pickOp) {
mMessagesCollectedCount = 0.0f;
mSampledAppOpCode = pickOp ? ThreadLocalRandom.current().nextInt(_NUM_OP) : OP_NONE;
- mAcceptableLeftDistance = _NUM_OP;
+ mAcceptableLeftDistance = _NUM_OP - 1;
mSampledPackage = packageName;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 6afe61e..e4387c9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -135,19 +135,27 @@
return;
}
- if (Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)
- && Utils.isKeyguard(getContext(), opPackageName)) {
- // If this happens, something in KeyguardUpdateMonitor is wrong.
- // SafetyNet for b/79776455
- EventLog.writeEvent(0x534e4554, "79776455");
- Slog.e(TAG, "Authenticate invoked when user is encrypted or lockdown");
- return;
+ // Keyguard check must be done on the caller's binder identity, since it also checks
+ // permission.
+ final boolean isKeyguard = Utils.isKeyguard(getContext(), opPackageName);
+
+ // Clear calling identity when checking LockPatternUtils for StrongAuth flags.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (isKeyguard && Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
+ // If this happens, something in KeyguardUpdateMonitor is wrong.
+ // SafetyNet for b/79776455
+ EventLog.writeEvent(0x534e4554, "79776455");
+ Slog.e(TAG, "Authenticate invoked when user is encrypted or lockdown");
+ return;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
final boolean restricted = getContext().checkCallingPermission(MANAGE_FINGERPRINT)
!= PackageManager.PERMISSION_GRANTED;
- final int statsClient = Utils.isKeyguard(getContext(), opPackageName)
- ? BiometricsProtoEnums.CLIENT_KEYGUARD
+ final int statsClient = isKeyguard ? BiometricsProtoEnums.CLIENT_KEYGUARD
: BiometricsProtoEnums.CLIENT_FINGERPRINT_MANAGER;
mFingerprint21.scheduleAuthenticate(token, operationId, userId, 0 /* cookie */,
new ClientMonitorCallbackConverter(receiver), opPackageName, surface,
diff --git a/services/core/java/com/android/server/content/SyncAdapterStateFetcher.java b/services/core/java/com/android/server/content/SyncAdapterStateFetcher.java
index 62fb751..ffaf364 100644
--- a/services/core/java/com/android/server/content/SyncAdapterStateFetcher.java
+++ b/services/core/java/com/android/server/content/SyncAdapterStateFetcher.java
@@ -15,11 +15,11 @@
*/
package com.android.server.content;
+import android.app.ActivityManagerInternal;
import android.app.usage.UsageStatsManagerInternal;
import android.os.SystemClock;
import android.util.Pair;
-import com.android.server.AppStateTracker;
import com.android.server.LocalServices;
import java.util.HashMap;
@@ -57,12 +57,7 @@
* Return UID active state.
*/
public boolean isAppActive(int uid) {
- final AppStateTracker ast =
- LocalServices.getService(AppStateTracker.class);
- if (ast == null) {
- return false;
- }
-
- return ast.isUidActive(uid);
+ final ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
+ return (ami != null) ? ami.isUidActive(uid) : false;
}
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 5ff40d0..48fa1bf 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -134,7 +134,7 @@
hdrCapabilities, isDefaultDisplay);
mDevices.put(physicalDisplayId, device);
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
- } else if (device.updateDisplayPropertiesLocked(configs, activeConfig,
+ } else if (device.updateDisplayPropertiesLocked(info, configs, activeConfig,
configSpecs, colorModes, activeColorMode, hdrCapabilities)) {
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
}
@@ -212,8 +212,7 @@
super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId);
mPhysicalDisplayId = physicalDisplayId;
mIsDefaultDisplay = isDefaultDisplay;
- mDisplayInfo = info;
- updateDisplayPropertiesLocked(configs, activeConfigId, configSpecs, colorModes,
+ updateDisplayPropertiesLocked(info, configs, activeConfigId, configSpecs, colorModes,
activeColorMode, hdrCapabilities);
mSidekickInternal = LocalServices.getService(SidekickInternal.class);
if (mIsDefaultDisplay) {
@@ -238,12 +237,15 @@
/**
* Returns true if there is a change.
**/
- public boolean updateDisplayPropertiesLocked(SurfaceControl.DisplayConfig[] configs,
+ public boolean updateDisplayPropertiesLocked(SurfaceControl.DisplayInfo info,
+ SurfaceControl.DisplayConfig[] configs,
int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs,
int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities) {
boolean changed = updateDisplayConfigsLocked(configs, activeConfigId, configSpecs);
+ changed |= updateDisplayInfo(info);
changed |= updateColorModesLocked(colorModes, activeColorMode);
changed |= updateHdrCapabilitiesLocked(hdrCapabilities);
+
if (changed) {
mHavePendingChanges = true;
}
@@ -420,6 +422,14 @@
mSystemBrightnessToNits = sysToNits;
}
+ private boolean updateDisplayInfo(SurfaceControl.DisplayInfo info) {
+ if (Objects.equals(mDisplayInfo, info)) {
+ return false;
+ }
+ mDisplayInfo = info;
+ return true;
+ }
+
private boolean updateColorModesLocked(int[] colorModes, int activeColorMode) {
if (colorModes == null) {
return false;
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/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index d9b25ba..74ed815 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -236,7 +236,7 @@
private static native void nativeSetInteractive(long ptr, boolean interactive);
private static native void nativeReloadCalibration(long ptr);
private static native void nativeVibrate(long ptr, int deviceId, long[] pattern,
- int repeat, int token);
+ int[] amplitudes, int repeat, int token);
private static native void nativeCancelVibrate(long ptr, int deviceId, int token);
private static native void nativeReloadKeyboardLayouts(long ptr);
private static native void nativeReloadDeviceAliases(long ptr);
@@ -1716,7 +1716,7 @@
// Binder call
@Override
- public void vibrate(int deviceId, long[] pattern, int repeat, IBinder token) {
+ public void vibrate(int deviceId, long[] pattern, int[] amplitudes, int repeat, IBinder token) {
if (repeat >= pattern.length) {
throw new ArrayIndexOutOfBoundsException();
}
@@ -1738,7 +1738,7 @@
synchronized (v) {
v.mVibrating = true;
- nativeVibrate(mPtr, deviceId, pattern, repeat, v.mTokenValue);
+ nativeVibrate(mPtr, deviceId, pattern, amplitudes, repeat, v.mTokenValue);
}
}
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/ConcurrentLinkedEvictingDeque.java b/services/core/java/com/android/server/location/ConcurrentLinkedEvictingDeque.java
new file mode 100644
index 0000000..6910c35
--- /dev/null
+++ b/services/core/java/com/android/server/location/ConcurrentLinkedEvictingDeque.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package com.android.server.location;
+
+import java.util.concurrent.ConcurrentLinkedDeque;
+
+/**
+ * Helper class to make a ConcurrentLinkedDeque fixed-size, evicting old entries when full.
+ *
+ * @param <E> The type of elements held in this queue.
+ */
+public class ConcurrentLinkedEvictingDeque<E> extends ConcurrentLinkedDeque<E> {
+ private int mSize;
+
+ ConcurrentLinkedEvictingDeque(int size) {
+ mSize = size;
+ }
+
+ @Override
+ public boolean add(E elem) {
+ synchronized (this) {
+ if (size() == mSize) {
+ poll();
+ }
+
+ return super.add(elem);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java
index 0f70bb8..33ceeef 100644
--- a/services/core/java/com/android/server/location/ContextHubClientManager.java
+++ b/services/core/java/com/android/server/location/ContextHubClientManager.java
@@ -36,7 +36,6 @@
import java.util.Date;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.function.Consumer;
/**
@@ -104,28 +103,6 @@
public static final int ACTION_CANCELLED = 2;
/**
- * Helper class to make a ConcurrentLinkedDeque fixed-size, evicting old entries when full.
- */
- private class ConcurrentLinkedEvictingDeque<E> extends ConcurrentLinkedDeque<E> {
- private int mSize;
-
- ConcurrentLinkedEvictingDeque(int size) {
- mSize = size;
- }
-
- @Override
- public boolean add(E elem) {
- synchronized (this) {
- if (size() == mSize) {
- poll();
- }
-
- return super.add(elem);
- }
- }
- }
-
- /**
* A container class to store a record of ContextHubClient registration.
*/
private class RegistrationRecord {
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index 48b2270..264c611 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -43,6 +43,7 @@
import android.hardware.location.NanoAppMessage;
import android.hardware.location.NanoAppState;
import android.location.LocationManager;
+import android.os.Binder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -386,7 +387,7 @@
createLoadTransactionCallback(contextHubHandle, nanoAppBinary);
ContextHubServiceTransaction transaction = mTransactionManager.createLoadTransaction(
- contextHubHandle, nanoAppBinary, onCompleteCallback);
+ contextHubHandle, nanoAppBinary, onCompleteCallback, getCallingPackageName());
mTransactionManager.addTransaction(transaction);
return 0;
@@ -411,7 +412,7 @@
IContextHubTransactionCallback onCompleteCallback =
createUnloadTransactionCallback(contextHubId);
ContextHubServiceTransaction transaction = mTransactionManager.createUnloadTransaction(
- contextHubId, nanoAppId, onCompleteCallback);
+ contextHubId, nanoAppId, onCompleteCallback, getCallingPackageName());
mTransactionManager.addTransaction(transaction);
return 0;
@@ -464,7 +465,7 @@
IContextHubTransactionCallback onCompleteCallback =
createQueryTransactionCallback(contextHubId);
ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
- contextHubId, onCompleteCallback);
+ contextHubId, onCompleteCallback, getCallingPackageName());
mTransactionManager.addTransaction(transaction);
return Result.OK;
@@ -696,7 +697,7 @@
}
ContextHubServiceTransaction transaction = mTransactionManager.createLoadTransaction(
- contextHubId, nanoAppBinary, transactionCallback);
+ contextHubId, nanoAppBinary, transactionCallback, getCallingPackageName());
mTransactionManager.addTransaction(transaction);
}
@@ -720,7 +721,7 @@
}
ContextHubServiceTransaction transaction = mTransactionManager.createUnloadTransaction(
- contextHubId, nanoAppId, transactionCallback);
+ contextHubId, nanoAppId, transactionCallback, getCallingPackageName());
mTransactionManager.addTransaction(transaction);
}
@@ -744,7 +745,7 @@
}
ContextHubServiceTransaction transaction = mTransactionManager.createEnableTransaction(
- contextHubId, nanoAppId, transactionCallback);
+ contextHubId, nanoAppId, transactionCallback, getCallingPackageName());
mTransactionManager.addTransaction(transaction);
}
@@ -768,7 +769,7 @@
}
ContextHubServiceTransaction transaction = mTransactionManager.createDisableTransaction(
- contextHubId, nanoAppId, transactionCallback);
+ contextHubId, nanoAppId, transactionCallback, getCallingPackageName());
mTransactionManager.addTransaction(transaction);
}
@@ -790,7 +791,7 @@
}
ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
- contextHubId, transactionCallback);
+ contextHubId, transactionCallback, getCallingPackageName());
mTransactionManager.addTransaction(transaction);
}
@@ -822,6 +823,10 @@
pw.println("=================== CLIENTS ====================");
pw.println(mClientManager);
+ pw.println("");
+ pw.println("=================== TRANSACTIONS ====================");
+ pw.println(mTransactionManager);
+
// dump eventLog
}
@@ -924,4 +929,8 @@
mContextHubWrapper.onSettingChanged(Setting.LOCATION,
enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
}
+
+ private String getCallingPackageName() {
+ return mContext.getPackageManager().getNameForUid(Binder.getCallingUid());
+ }
}
diff --git a/services/core/java/com/android/server/location/ContextHubServiceTransaction.java b/services/core/java/com/android/server/location/ContextHubServiceTransaction.java
index c1fc982..62bd91b 100644
--- a/services/core/java/com/android/server/location/ContextHubServiceTransaction.java
+++ b/services/core/java/com/android/server/location/ContextHubServiceTransaction.java
@@ -32,14 +32,32 @@
@ContextHubTransaction.Type
private final int mTransactionType;
+ /* The ID of the nanoapp this transaction is targeted for, null if not applicable. */
+ private final Long mNanoAppId;
+
+ /*
+ * The host package associated with this transaction.
+ */
+ private final String mPackage;
+
/*
* true if the transaction has already completed, false otherwise
*/
private boolean mIsComplete = false;
- /* package */ ContextHubServiceTransaction(int id, int type) {
+ /* package */ ContextHubServiceTransaction(int id, int type, String packageName) {
mTransactionId = id;
mTransactionType = type;
+ mNanoAppId = null;
+ mPackage = packageName;
+ }
+
+ /* package */ ContextHubServiceTransaction(int id, int type, long nanoAppId,
+ String packageName) {
+ mTransactionId = id;
+ mTransactionType = type;
+ mNanoAppId = nanoAppId;
+ mPackage = packageName;
}
/**
@@ -129,7 +147,13 @@
@Override
public String toString() {
- return ContextHubTransaction.typeToString(mTransactionType, true /* upperCase */)
- + " transaction (ID = " + mTransactionId + ")";
+ String out = ContextHubTransaction.typeToString(mTransactionType, true /* upperCase */)
+ + " (";
+ if (mNanoAppId != null) {
+ out += "appId = 0x" + Long.toHexString(mNanoAppId) + ", ";
+ }
+ out += "package = " + mPackage + ")";
+
+ return out;
}
}
diff --git a/services/core/java/com/android/server/location/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
index cced781..d3fc705 100644
--- a/services/core/java/com/android/server/location/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
@@ -26,11 +26,15 @@
import android.os.RemoteException;
import android.util.Log;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
import java.util.ArrayDeque;
import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
import java.util.List;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@@ -53,6 +57,11 @@
private static final int MAX_PENDING_REQUESTS = 10000;
/*
+ * The DateFormat for printing TransactionRecord.
+ */
+ private static final DateFormat DATE_FORMAT = new SimpleDateFormat("MM/dd HH:mm:ss.SSS");
+
+ /*
* The proxy to talk to the Context Hub
*/
private final IContexthub mContextHubProxy;
@@ -83,6 +92,33 @@
private final ScheduledThreadPoolExecutor mTimeoutExecutor = new ScheduledThreadPoolExecutor(1);
private ScheduledFuture<?> mTimeoutFuture = null;
+ /*
+ * The list of previous transaction records.
+ */
+ private static final int NUM_TRANSACTION_RECORDS = 20;
+ private final ConcurrentLinkedEvictingDeque<TransactionRecord> mTransactionRecordDeque =
+ new ConcurrentLinkedEvictingDeque<>(NUM_TRANSACTION_RECORDS);
+
+ /**
+ * A container class to store a record of transactions.
+ */
+ private class TransactionRecord {
+ private final String mTransaction;
+ private final long mTimestamp;
+
+ TransactionRecord(String transaction) {
+ mTransaction = transaction;
+ mTimestamp = System.currentTimeMillis();
+ }
+
+ // TODO: Add dump to proto here
+
+ @Override
+ public String toString() {
+ return DATE_FORMAT.format(new Date(mTimestamp)) + " " + mTransaction;
+ }
+ }
+
/* package */ ContextHubTransactionManager(
IContexthub contextHubProxy, ContextHubClientManager clientManager,
NanoAppStateManager nanoAppStateManager) {
@@ -101,11 +137,12 @@
*/
/* package */ ContextHubServiceTransaction createLoadTransaction(
int contextHubId, NanoAppBinary nanoAppBinary,
- IContextHubTransactionCallback onCompleteCallback) {
+ IContextHubTransactionCallback onCompleteCallback, String packageName) {
return new ContextHubServiceTransaction(
- mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_LOAD_NANOAPP) {
+ mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_LOAD_NANOAPP,
+ nanoAppBinary.getNanoAppId(), packageName) {
@Override
- /* package */ int onTransact() {
+ /* package */ int onTransact() {
android.hardware.contexthub.V1_0.NanoAppBinary hidlNanoAppBinary =
ContextHubServiceUtil.createHidlNanoAppBinary(nanoAppBinary);
try {
@@ -119,7 +156,7 @@
}
@Override
- /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+ /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
if (result == ContextHubTransaction.RESULT_SUCCESS) {
// NOTE: The legacy JNI code used to do a query right after a load success
// to synchronize the service cache. Instead store the binary that was
@@ -149,11 +186,13 @@
* @return the generated transaction
*/
/* package */ ContextHubServiceTransaction createUnloadTransaction(
- int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback) {
+ int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback,
+ String packageName) {
return new ContextHubServiceTransaction(
- mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_UNLOAD_NANOAPP) {
+ mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_UNLOAD_NANOAPP,
+ nanoAppId, packageName) {
@Override
- /* package */ int onTransact() {
+ /* package */ int onTransact() {
try {
return mContextHubProxy.unloadNanoApp(
contextHubId, nanoAppId, this.getTransactionId());
@@ -165,7 +204,7 @@
}
@Override
- /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+ /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
if (result == ContextHubTransaction.RESULT_SUCCESS) {
mNanoAppStateManager.removeNanoAppInstance(contextHubId, nanoAppId);
}
@@ -190,11 +229,13 @@
* @return the generated transaction
*/
/* package */ ContextHubServiceTransaction createEnableTransaction(
- int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback) {
+ int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback,
+ String packageName) {
return new ContextHubServiceTransaction(
- mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_ENABLE_NANOAPP) {
+ mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_ENABLE_NANOAPP,
+ packageName) {
@Override
- /* package */ int onTransact() {
+ /* package */ int onTransact() {
try {
return mContextHubProxy.enableNanoApp(
contextHubId, nanoAppId, this.getTransactionId());
@@ -206,7 +247,7 @@
}
@Override
- /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+ /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
try {
onCompleteCallback.onTransactionComplete(result);
} catch (RemoteException e) {
@@ -225,11 +266,13 @@
* @return the generated transaction
*/
/* package */ ContextHubServiceTransaction createDisableTransaction(
- int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback) {
+ int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback,
+ String packageName) {
return new ContextHubServiceTransaction(
- mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_DISABLE_NANOAPP) {
+ mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_DISABLE_NANOAPP,
+ packageName) {
@Override
- /* package */ int onTransact() {
+ /* package */ int onTransact() {
try {
return mContextHubProxy.disableNanoApp(
contextHubId, nanoAppId, this.getTransactionId());
@@ -241,7 +284,7 @@
}
@Override
- /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+ /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
try {
onCompleteCallback.onTransactionComplete(result);
} catch (RemoteException e) {
@@ -259,11 +302,13 @@
* @return the generated transaction
*/
/* package */ ContextHubServiceTransaction createQueryTransaction(
- int contextHubId, IContextHubTransactionCallback onCompleteCallback) {
+ int contextHubId, IContextHubTransactionCallback onCompleteCallback,
+ String packageName) {
return new ContextHubServiceTransaction(
- mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_QUERY_NANOAPPS) {
+ mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_QUERY_NANOAPPS,
+ packageName) {
@Override
- /* package */ int onTransact() {
+ /* package */ int onTransact() {
try {
return mContextHubProxy.queryApps(contextHubId);
} catch (RemoteException e) {
@@ -273,12 +318,12 @@
}
@Override
- /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+ /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
onQueryResponse(result, Collections.emptyList());
}
@Override
- /* package */ void onQueryResponse(
+ /* package */ void onQueryResponse(
@ContextHubTransaction.Result int result, List<NanoAppState> nanoAppStateList) {
try {
onCompleteCallback.onQueryResponse(result, nanoAppStateList);
@@ -307,6 +352,7 @@
+ MAX_PENDING_REQUESTS + ")");
}
mTransactionQueue.add(transaction);
+ mTransactionRecordDeque.add(new TransactionRecord(transaction.toString()));
if (mTransactionQueue.size() == 1) {
startNextTransaction();
@@ -433,4 +479,23 @@
}
}
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(100);
+ TransactionRecord[] arr;
+ synchronized (this) {
+ arr = mTransactionQueue.toArray(new TransactionRecord[0]);
+ }
+ for (int i = 0; i < arr.length; i++) {
+ sb.append(i + ": " + arr[i] + "\n");
+ }
+
+ sb.append("Transaction History:\n");
+ Iterator<TransactionRecord> iterator = mTransactionRecordDeque.descendingIterator();
+ while (iterator.hasNext()) {
+ sb.append(iterator.next() + "\n");
+ }
+ return sb.toString();
+ }
}
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index f69c823..d3558f1 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;
@@ -36,6 +39,7 @@
import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
+import static com.android.server.location.LocationPermissions.PERMISSION_NONE;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
@@ -120,10 +124,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 +183,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 +496,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 +1242,22 @@
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,
- LocationPermissions.getPermissionLevel(mContext, mCallerIdentity.getUid(),
- mCallerIdentity.getPid()))) {
+ int permissionLevel = LocationPermissions.getPermissionLevel(mContext,
+ mCallerIdentity.getUid(), mCallerIdentity.getPid());
+ if (!allowMonitoring || permissionLevel == PERMISSION_NONE
+ || !mAppOpsHelper.checkOpNoThrow(
+ LocationPermissions.asAppOp(permissionLevel), 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 +1602,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 +2132,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 +2194,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 +2345,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 +2366,6 @@
LocationStatsEnums.USAGE_ENDED,
LocationStatsEnums.API_SEND_EXTRA_COMMAND,
provider);
-
- return true;
}
@Override
@@ -2553,7 +2567,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 +2659,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 +2679,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 +2702,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 +2723,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 +2941,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 +2984,11 @@
}
@Override
+ public LocationPermissionsHelper getLocationPermissionsHelper() {
+ return mLocationPermissionsHelper;
+ }
+
+ @Override
public SettingsHelper getSettingsHelper() {
return mSettingsHelper;
}
@@ -2972,8 +3004,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 8aaf4bf..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 88964e0..cf08e73 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -274,6 +274,7 @@
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.BackgroundActivityStartCallback;
import com.android.server.wm.WindowManagerInternal;
import libcore.io.IoUtils;
@@ -515,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;
@@ -1722,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
@@ -1894,6 +1895,7 @@
(AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
mAm = am;
mAtm = atm;
+ mAtm.setBackgroundActivityStartCallback(new NotificationTrampolineCallback());
mUgm = ugm;
mUgmInternal = ugmInternal;
mPackageManager = packageManager;
@@ -4752,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());
@@ -5775,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));
}
}
@@ -7642,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
@@ -9918,4 +9926,36 @@
if (TextUtils.isEmpty(val)) return defValue;
return Boolean.parseBoolean(val);
}
+
+ /**
+ * Shows a warning on logcat. Shows the toast only once per package. This is to avoid being too
+ * aggressive and annoying the user.
+ *
+ * TODO(b/161957908): Remove dogfooder toast.
+ */
+ private class NotificationTrampolineCallback implements BackgroundActivityStartCallback {
+ private Set<String> mPackagesShown = new ArraySet<>();
+
+ @Override
+ public IBinder getToken() {
+ return ALLOWLIST_TOKEN;
+ }
+
+ @Override
+ public void onExclusiveTokenActivityStart(String packageName) {
+ Slog.w(TAG, "Indirect notification activity start from " + packageName);
+ boolean isFirstOccurrence = mPackagesShown.add(packageName);
+ if (!isFirstOccurrence) {
+ return;
+ }
+
+ mUiHandler.post(() ->
+ Toast.makeText(getUiContext(),
+ "Indirect activity start from "
+ + packageName + ". "
+ + "This will be blocked in S.\n"
+ + "See go/s-trampolines.",
+ Toast.LENGTH_LONG).show());
+ }
+ }
}
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..92da005 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -164,10 +164,11 @@
private static final boolean LOGD = true;
private static final String REMOVE_MARKER_EXTENSION = ".removed";
- private static final int MSG_STREAM_VALIDATE_AND_COMMIT = 1;
- private static final int MSG_INSTALL = 2;
- private static final int MSG_ON_PACKAGE_INSTALLED = 3;
- private static final int MSG_SESSION_VERIFICATION_FAILURE = 4;
+ private static final int MSG_ON_SESSION_SEALED = 1;
+ private static final int MSG_STREAM_VALIDATE_AND_COMMIT = 2;
+ private static final int MSG_INSTALL = 3;
+ private static final int MSG_ON_PACKAGE_INSTALLED = 4;
+ private static final int MSG_SESSION_VERIFICATION_FAILURE = 5;
/** XML constants used for persisting a session */
static final String TAG_SESSION = "session";
@@ -444,6 +445,9 @@
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
+ case MSG_ON_SESSION_SEALED:
+ handleSessionSealed();
+ break;
case MSG_STREAM_VALIDATE_AND_COMMIT:
handleStreamValidateAndCommit();
break;
@@ -459,12 +463,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 +494,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 +516,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 +536,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 +548,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 +748,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);
@@ -1156,6 +1176,22 @@
}
}
+ dispatchSessionSealed();
+ }
+
+ /**
+ * Kicks off the install flow. The first step is to persist 'sealed' flags
+ * to prevent mutations of hard links created later.
+ */
+ private void dispatchSessionSealed() {
+ mHandler.obtainMessage(MSG_ON_SESSION_SEALED).sendToTarget();
+ }
+
+ private void handleSessionSealed() {
+ assertSealed("dispatchSessionSealed");
+ // Persist the fact that we've sealed ourselves to prevent
+ // mutations of any hard links we create.
+ mCallback.onSessionSealedBlocking(this);
dispatchStreamValidateAndCommit();
}
@@ -1409,11 +1445,6 @@
}
}
- // Persist the fact that we've sealed ourselves to prevent
- // mutations of any hard links we create. We do this without holding
- // the session lock, since otherwise it's a lock inversion.
- mCallback.onSessionSealedBlocking(this);
-
return true;
}
@@ -1685,19 +1716,10 @@
mInstallerUid = newOwnerAppInfo.uid;
mInstallSource = InstallSource.create(packageName, null, packageName, null);
}
-
- // Persist the fact that we've sealed ourselves to prevent
- // mutations of any hard links we create. We do this without holding
- // the session lock, since otherwise it's a lock inversion.
- mCallback.onSessionSealedBlocking(this);
}
private void handleInstall() {
- final boolean needsLogging;
- synchronized (mLock) {
- needsLogging = isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked();
- }
- if (needsLogging) {
+ if (isInstallerDeviceOwnerOrAffiliatedProfileOwner()) {
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
.setAdmin(mInstallSource.installerPackageName)
@@ -1724,9 +1746,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 +1755,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 +1771,7 @@
final PackageInstallerSession session = childSessions.get(i);
try {
final PackageManagerService.ActiveInstallSession installingChildSession =
- session.makeSessionActiveLocked();
+ session.makeSessionActive();
if (installingChildSession != null) {
installingChildSessions.add(installingChildSession);
}
@@ -1762,8 +1781,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 +1801,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 +2478,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) {
@@ -2893,9 +2933,9 @@
}
if (hasParentSessionId()) {
mSessionProvider.getSession(
- getParentSessionId()).dispatchStreamValidateAndCommit();
+ getParentSessionId()).dispatchSessionSealed();
} else {
- dispatchStreamValidateAndCommit();
+ dispatchSessionSealed();
}
if (manualStartAndDestroy) {
dataLoader.destroy(dataLoaderId);
@@ -2915,7 +2955,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 +2974,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 +3053,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 +3373,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 +3388,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 +3431,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 +3451,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 +3525,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 +3590,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 +3598,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..73db48a 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,7 @@
= 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 +6444,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 +6615,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 +6641,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 +6823,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 +6929,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 +6959,7 @@
continue;
}
- if (removeMatches) {
+ if (removeMatches && allowSetMutation) {
pir.removeFilter(pa);
changed = true;
if (DEBUG_PREFERRED) {
@@ -6945,7 +6976,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 +6997,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 +10521,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 +12225,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 +21949,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/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index c37f09a..b0d3d53 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3306,27 +3306,6 @@
}
}
- private long logUserCreateJourneyBegin(@UserIdInt int userId, String userType,
- @UserInfoFlag int flags) {
- final long sessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE);
- // log the journey atom with the user metadata
- FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId,
- FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE,
- /* origin_user= */ -1, userId, UserManager.getUserTypeForStatsd(userType), flags);
- // log the event atom to indicate the event start
- FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId,
- FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER,
- FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN);
- return sessionId;
- }
-
- private void logUserCreateJourneyFinish(long sessionId, @UserIdInt int userId, boolean finish) {
- FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId,
- FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER,
- finish ? FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__FINISH
- : FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE);
- }
-
private UserInfo createUserInternalUncheckedNoTracing(@Nullable String name,
@NonNull String userType, @UserInfoFlag int flags, @UserIdInt int parentId,
boolean preCreate, @Nullable String[] disallowedPackages,
@@ -3433,6 +3412,7 @@
}
userId = getNextAvailableId();
+ Slog.i(LOG_TAG, "Creating user " + userId + " of type " + userType);
Environment.getUserSystemDirectory(userId).mkdirs();
synchronized (mUsersLock) {
@@ -3684,6 +3664,27 @@
&& !userTypeDetails.getName().equals(UserManager.USER_TYPE_FULL_RESTRICTED);
}
+ private long logUserCreateJourneyBegin(@UserIdInt int userId, String userType,
+ @UserInfoFlag int flags) {
+ final long sessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE);
+ // log the journey atom with the user metadata
+ FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId,
+ FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE,
+ /* origin_user= */ -1, userId, UserManager.getUserTypeForStatsd(userType), flags);
+ // log the event atom to indicate the event start
+ FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId,
+ FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER,
+ FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN);
+ return sessionId;
+ }
+
+ private void logUserCreateJourneyFinish(long sessionId, @UserIdInt int userId, boolean finish) {
+ FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId,
+ FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER,
+ finish ? FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__FINISH
+ : FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE);
+ }
+
@VisibleForTesting
UserData putUserInfo(UserInfo userInfo) {
final UserData userData = new UserData();
@@ -4698,6 +4699,7 @@
pw.println("N/A");
}
+ pw.println();
StringBuilder sb = new StringBuilder();
synchronized (mPackagesLock) {
synchronized (mUsersLock) {
@@ -4785,6 +4787,7 @@
}
}
pw.println();
+ pw.println("Device properties:");
pw.println(" Device owner id:" + mDeviceOwnerUserId);
pw.println();
pw.println(" Guest restrictions:");
@@ -4818,10 +4821,10 @@
// Dump UserTypes
pw.println();
- pw.println(" User types (" + mUserTypes.size() + " types):");
+ pw.println("User types (" + mUserTypes.size() + " types):");
for (int i = 0; i < mUserTypes.size(); i++) {
pw.println(" " + mUserTypes.keyAt(i) + ": ");
- mUserTypes.valueAt(i).dump(pw);
+ mUserTypes.valueAt(i).dump(pw, " ");
}
// Dump package whitelist
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index be6880e..d840e5d 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -271,8 +271,7 @@
}
/** Dumps details of the UserTypeDetails. Do not parse this. */
- public void dump(PrintWriter pw) {
- final String prefix = " ";
+ public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mName: "); pw.println(mName);
pw.print(prefix); pw.print("mBaseType: "); pw.println(UserInfo.flagsToString(mBaseType));
pw.print(prefix); pw.print("mEnabled: "); pw.println(mEnabled);
@@ -282,6 +281,7 @@
pw.println(UserInfo.flagsToString(mDefaultUserInfoPropertyFlags));
pw.print(prefix); pw.print("mLabel: "); pw.println(mLabel);
+ final String restrictionsPrefix = prefix + " ";
if (isSystem()) {
pw.print(prefix); pw.println("config_defaultFirstUserRestrictions: ");
try {
@@ -293,13 +293,13 @@
restrictions.putBoolean(userRestriction, true);
}
}
- UserRestrictionsUtils.dumpRestrictions(pw, prefix + " ", restrictions);
+ UserRestrictionsUtils.dumpRestrictions(pw, restrictionsPrefix, restrictions);
} catch (Resources.NotFoundException e) {
- pw.print(prefix); pw.println(" none - resource not found");
+ pw.print(restrictionsPrefix); pw.println("none - resource not found");
}
} else {
pw.print(prefix); pw.println("mDefaultRestrictions: ");
- UserRestrictionsUtils.dumpRestrictions(pw, prefix + " ", mDefaultRestrictions);
+ UserRestrictionsUtils.dumpRestrictions(pw, restrictionsPrefix, mDefaultRestrictions);
}
pw.print(prefix); pw.print("mIconBadge: "); pw.println(mIconBadge);
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/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index e75dab7..03868e9 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -209,7 +209,6 @@
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.vr.VrManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
-import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
import com.android.server.wm.AppTransition;
import com.android.server.wm.DisplayPolicy;
import com.android.server.wm.DisplayRotation;
@@ -491,7 +490,7 @@
private boolean mPendingKeyguardOccluded;
private boolean mKeyguardOccludedChanged;
- SleepToken mScreenOffSleepToken;
+ private ActivityTaskManagerInternal.SleepTokenAcquirer mScreenOffSleepTokenAcquirer;
volatile boolean mKeyguardOccluded;
Intent mHomeIntent;
Intent mCarDockIntent;
@@ -1741,6 +1740,9 @@
new AccessibilityShortcutController(mContext, new Handler(), mCurrentUserId);
mLogger = new MetricsLogger();
+ mScreenOffSleepTokenAcquirer = mActivityTaskManagerInternal
+ .createSleepTokenAcquirer("ScreenOff");
+
Resources res = mContext.getResources();
mWakeOnDpadKeyPress =
res.getBoolean(com.android.internal.R.bool.config_wakeOnDpadKeyPress);
@@ -4984,15 +4986,9 @@
// TODO (multidisplay): Support multiple displays in WindowManagerPolicy.
private void updateScreenOffSleepToken(boolean acquire) {
if (acquire) {
- if (mScreenOffSleepToken == null) {
- mScreenOffSleepToken = mActivityTaskManagerInternal.acquireSleepToken(
- "ScreenOff", DEFAULT_DISPLAY);
- }
+ mScreenOffSleepTokenAcquirer.acquire(DEFAULT_DISPLAY);
} else {
- if (mScreenOffSleepToken != null) {
- mScreenOffSleepToken.release();
- mScreenOffSleepToken = null;
- }
+ mScreenOffSleepTokenAcquirer.release(DEFAULT_DISPLAY);
}
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 7b06759..4c4680b 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -44,8 +44,8 @@
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.hardware.power.Boost;
import android.hardware.power.Mode;
-import android.hardware.power.V1_0.PowerHint;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
@@ -203,9 +203,6 @@
// How long a partial wake lock must be held until we consider it a long wake lock.
static final long MIN_LONG_WAKE_CHECK_INTERVAL = 60*1000;
- // Power features defined in hardware/libhardware/include/hardware/power.h.
- private static final int POWER_FEATURE_DOUBLE_TAP_TO_WAKE = 1;
-
// Default setting for double tap to wake.
private static final int DEFAULT_DOUBLE_TAP_TO_WAKE = 0;
@@ -325,7 +322,7 @@
private long mLastUserActivityTime;
private long mLastUserActivityTimeNoChangeLights;
- // Timestamp of last interactive power hint.
+ // Timestamp of last time power boost interaction was sent.
private long mLastInteractivePowerHintTime;
// Timestamp of the last screen brightness boost.
@@ -719,21 +716,11 @@
PowerManagerService.nativeReleaseSuspendBlocker(name);
}
- /** Wrapper for PowerManager.nativeSetInteractive */
- public void nativeSetInteractive(boolean enable) {
- PowerManagerService.nativeSetInteractive(enable);
- }
-
/** Wrapper for PowerManager.nativeSetAutoSuspend */
public void nativeSetAutoSuspend(boolean enable) {
PowerManagerService.nativeSetAutoSuspend(enable);
}
- /** Wrapper for PowerManager.nativeSendPowerHint */
- public void nativeSendPowerHint(int hintId, int data) {
- PowerManagerService.nativeSendPowerHint(hintId, data);
- }
-
/** Wrapper for PowerManager.nativeSetPowerBoost */
public void nativeSetPowerBoost(int boost, int durationMs) {
PowerManagerService.nativeSetPowerBoost(boost, durationMs);
@@ -744,11 +731,6 @@
return PowerManagerService.nativeSetPowerMode(mode, enabled);
}
- /** Wrapper for PowerManager.nativeSetFeature */
- public void nativeSetFeature(int featureId, int data) {
- PowerManagerService.nativeSetFeature(featureId, data);
- }
-
/** Wrapper for PowerManager.nativeForceSuspend */
public boolean nativeForceSuspend() {
return PowerManagerService.nativeForceSuspend();
@@ -851,12 +833,9 @@
private native void nativeInit();
private static native void nativeAcquireSuspendBlocker(String name);
private static native void nativeReleaseSuspendBlocker(String name);
- private static native void nativeSetInteractive(boolean enable);
private static native void nativeSetAutoSuspend(boolean enable);
- private static native void nativeSendPowerHint(int hintId, int data);
private static native void nativeSetPowerBoost(int boost, int durationMs);
private static native boolean nativeSetPowerMode(int mode, boolean enabled);
- private static native void nativeSetFeature(int featureId, int data);
private static native boolean nativeForceSuspend();
public PowerManagerService(Context context) {
@@ -1000,8 +979,8 @@
mNativeWrapper.nativeInit(this);
mNativeWrapper.nativeSetAutoSuspend(false);
- mNativeWrapper.nativeSetInteractive(true);
- mNativeWrapper.nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, 0);
+ mNativeWrapper.nativeSetPowerMode(Mode.INTERACTIVE, true);
+ mNativeWrapper.nativeSetPowerMode(Mode.DOUBLE_TAP_TO_WAKE, false);
mInjector.invalidateIsInteractiveCaches();
}
}
@@ -1252,8 +1231,7 @@
UserHandle.USER_CURRENT) != 0;
if (doubleTapWakeEnabled != mDoubleTapWakeEnabled) {
mDoubleTapWakeEnabled = doubleTapWakeEnabled;
- mNativeWrapper.nativeSetFeature(
- POWER_FEATURE_DOUBLE_TAP_TO_WAKE, mDoubleTapWakeEnabled ? 1 : 0);
+ mNativeWrapper.nativeSetPowerMode(Mode.DOUBLE_TAP_TO_WAKE, mDoubleTapWakeEnabled);
}
}
@@ -1612,7 +1590,7 @@
Trace.traceBegin(Trace.TRACE_TAG_POWER, "userActivity");
try {
if (eventTime > mLastInteractivePowerHintTime) {
- powerHintInternal(PowerHint.INTERACTION, 0);
+ setPowerBoostInternal(Boost.INTERACTION, 0);
mLastInteractivePowerHintTime = eventTime;
}
@@ -3171,7 +3149,7 @@
mHalInteractiveModeEnabled = enable;
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setHalInteractive(" + enable + ")");
try {
- mNativeWrapper.nativeSetInteractive(enable);
+ mNativeWrapper.nativeSetPowerMode(Mode.INTERACTIVE, enable);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
@@ -3645,19 +3623,6 @@
mIsVrModeEnabled = enabled;
}
- private void powerHintInternal(int hintId, int data) {
- // Maybe filter the event.
- switch (hintId) {
- case PowerHint.LAUNCH: // 1: activate launch boost 0: deactivate.
- if (data == 1 && mBatterySaverController.isLaunchBoostDisabled()) {
- return;
- }
- break;
- }
-
- mNativeWrapper.nativeSendPowerHint(hintId, data);
- }
-
private void setPowerBoostInternal(int boost, int durationMs) {
// Maybe filter the event.
mNativeWrapper.nativeSetPowerBoost(boost, durationMs);
@@ -4404,7 +4369,7 @@
private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
@Override
public void onVrStateChanged(boolean enabled) {
- powerHintInternal(PowerHint.VR_MODE, enabled ? 1 : 0);
+ setPowerModeInternal(Mode.VR, enabled);
synchronized (mLock) {
if (mIsVrModeEnabled != enabled) {
@@ -4715,16 +4680,6 @@
}
@Override // Binder call
- public void powerHint(int hintId, int data) {
- if (!mSystemReady) {
- // Service not ready yet, so who the heck cares about power hints, bah.
- return;
- }
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
- powerHintInternal(hintId, data);
- }
-
- @Override // Binder call
public void setPowerBoost(int boost, int durationMs) {
if (!mSystemReady) {
// Service not ready yet, so who the heck cares about power hints, bah.
@@ -5559,11 +5514,6 @@
}
@Override
- public void powerHint(int hintId, int data) {
- powerHintInternal(hintId, data);
- }
-
- @Override
public void setPowerBoost(int boost, int durationMs) {
setPowerBoostInternal(boost, durationMs);
}
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index 2a4a69d..dd287ca 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -23,7 +23,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManagerInternal;
-import android.hardware.power.V1_0.PowerHint;
+import android.hardware.power.Mode;
import android.os.BatteryManager;
import android.os.BatterySaverPolicyConfig;
import android.os.Handler;
@@ -49,6 +49,7 @@
import com.android.server.power.batterysaver.BatterySavingStats.BatterySaverState;
import com.android.server.power.batterysaver.BatterySavingStats.DozeState;
import com.android.server.power.batterysaver.BatterySavingStats.InteractiveState;
+import com.android.server.power.batterysaver.BatterySavingStats.PlugState;
import java.util.ArrayList;
import java.util.Objects;
@@ -474,7 +475,7 @@
final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
if (pmi != null) {
- pmi.powerHint(PowerHint.LOW_POWER, isEnabled() ? 1 : 0);
+ pmi.setPowerMode(Mode.LOW_POWER, isEnabled());
}
updateBatterySavingStats();
@@ -551,17 +552,14 @@
: DozeState.NOT_DOZING;
synchronized (mLock) {
- if (mIsPluggedIn) {
- mBatterySavingStats.startCharging();
- return;
- }
mBatterySavingStats.transitionState(
getFullEnabledLocked() ? BatterySaverState.ON :
(getAdaptiveEnabledLocked() ? BatterySaverState.ADAPTIVE :
BatterySaverState.OFF),
isInteractive ? InteractiveState.INTERACTIVE :
InteractiveState.NON_INTERACTIVE,
- dozeMode);
+ dozeMode,
+ mIsPluggedIn ? PlugState.PLUGGED : PlugState.UNPLUGGED);
}
}
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
index 3dbc007..05695d9 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
@@ -17,8 +17,8 @@
import android.os.BatteryManagerInternal;
import android.os.SystemClock;
-import android.util.ArrayMap;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
@@ -93,6 +93,20 @@
}
}
+ /** Whether the device is plugged in or not. */
+ interface PlugState {
+ int UNPLUGGED = 0;
+ int PLUGGED = 1;
+
+ int SHIFT = DozeState.SHIFT + DozeState.BITS;
+ int BITS = 1;
+ int MASK = (1 << BITS) - 1;
+
+ static int fromIndex(int index) {
+ return (index >> SHIFT) & MASK;
+ }
+ }
+
/**
* Various stats in each state.
*/
@@ -140,7 +154,6 @@
private BatteryManagerInternal mBatteryManagerInternal;
private static final int STATE_NOT_INITIALIZED = -1;
- private static final int STATE_CHARGING = -2;
/**
* Current state, one of STATE_* or values returned by {@link #statesToIndex}.
@@ -153,20 +166,26 @@
*/
@VisibleForTesting
@GuardedBy("mLock")
- final ArrayMap<Integer, Stat> mStats = new ArrayMap<>();
+ final SparseArray<Stat> mStats = new SparseArray<>();
@GuardedBy("mLock")
private int mBatterySaverEnabledCount = 0;
@GuardedBy("mLock")
- private boolean mIsBatterySaverEnabled;
-
- @GuardedBy("mLock")
private long mLastBatterySaverEnabledTime = 0;
@GuardedBy("mLock")
private long mLastBatterySaverDisabledTime = 0;
+ @GuardedBy("mLock")
+ private int mAdaptiveBatterySaverEnabledCount = 0;
+
+ @GuardedBy("mLock")
+ private long mLastAdaptiveBatterySaverEnabledTime = 0;
+
+ @GuardedBy("mLock")
+ private long mLastAdaptiveBatterySaverDisabledTime = 0;
+
/** Visible for unit tests */
@VisibleForTesting
public BatterySavingStats(Object lock) {
@@ -189,10 +208,11 @@
*/
@VisibleForTesting
static int statesToIndex(
- int batterySaverState, int interactiveState, int dozeState) {
+ int batterySaverState, int interactiveState, int dozeState, int plugState) {
int ret = batterySaverState & BatterySaverState.MASK;
ret |= (interactiveState & InteractiveState.MASK) << InteractiveState.SHIFT;
ret |= (dozeState & DozeState.MASK) << DozeState.SHIFT;
+ ret |= (plugState & PlugState.MASK) << PlugState.SHIFT;
return ret;
}
@@ -204,12 +224,11 @@
switch (state) {
case STATE_NOT_INITIALIZED:
return "NotInitialized";
- case STATE_CHARGING:
- return "Charging";
}
return "BS=" + BatterySaverState.fromIndex(state)
+ ",I=" + InteractiveState.fromIndex(state)
- + ",D=" + DozeState.fromIndex(state);
+ + ",D=" + DozeState.fromIndex(state)
+ + ",P=" + PlugState.fromIndex(state);
}
/**
@@ -230,8 +249,9 @@
/**
* @return {@link Stat} fo a given state triplet.
*/
- private Stat getStat(int batterySaverState, int interactiveState, int dozeState) {
- return getStat(statesToIndex(batterySaverState, interactiveState, dozeState));
+ private Stat getStat(int batterySaverState, int interactiveState, int dozeState,
+ int plugState) {
+ return getStat(statesToIndex(batterySaverState, interactiveState, dozeState, plugState));
}
@VisibleForTesting
@@ -258,26 +278,17 @@
}
/**
- * Called from the outside whenever any of the states changes, when the device is not plugged
- * in.
+ * Called from the outside whenever any of the states change.
*/
- public void transitionState(int batterySaverState, int interactiveState, int dozeState) {
+ void transitionState(int batterySaverState, int interactiveState, int dozeState,
+ int plugState) {
synchronized (mLock) {
final int newState = statesToIndex(
- batterySaverState, interactiveState, dozeState);
+ batterySaverState, interactiveState, dozeState, plugState);
transitionStateLocked(newState);
}
}
- /**
- * Called from the outside when the device is plugged in.
- */
- public void startCharging() {
- synchronized (mLock) {
- transitionStateLocked(STATE_CHARGING);
- }
- }
-
@GuardedBy("mLock")
private void transitionStateLocked(int newState) {
if (mCurrentState == newState) {
@@ -287,17 +298,33 @@
final int batteryLevel = injectBatteryLevel();
final int batteryPercent = injectBatteryPercent();
- final boolean oldBatterySaverEnabled =
- BatterySaverState.fromIndex(mCurrentState) != BatterySaverState.OFF;
- final boolean newBatterySaverEnabled =
- BatterySaverState.fromIndex(newState) != BatterySaverState.OFF;
- if (oldBatterySaverEnabled != newBatterySaverEnabled) {
- mIsBatterySaverEnabled = newBatterySaverEnabled;
- if (newBatterySaverEnabled) {
- mBatterySaverEnabledCount++;
- mLastBatterySaverEnabledTime = injectCurrentTime();
- } else {
- mLastBatterySaverDisabledTime = injectCurrentTime();
+ final int oldBatterySaverState = mCurrentState < 0
+ ? BatterySaverState.OFF : BatterySaverState.fromIndex(mCurrentState);
+ final int newBatterySaverState = newState < 0
+ ? BatterySaverState.OFF : BatterySaverState.fromIndex(newState);
+ if (oldBatterySaverState != newBatterySaverState) {
+ switch (newBatterySaverState) {
+ case BatterySaverState.ON:
+ mBatterySaverEnabledCount++;
+ mLastBatterySaverEnabledTime = now;
+ if (oldBatterySaverState == BatterySaverState.ADAPTIVE) {
+ mLastAdaptiveBatterySaverDisabledTime = now;
+ }
+ break;
+ case BatterySaverState.OFF:
+ if (oldBatterySaverState == BatterySaverState.ON) {
+ mLastBatterySaverDisabledTime = now;
+ } else {
+ mLastAdaptiveBatterySaverDisabledTime = now;
+ }
+ break;
+ case BatterySaverState.ADAPTIVE:
+ mAdaptiveBatterySaverEnabledCount++;
+ mLastAdaptiveBatterySaverEnabledTime = now;
+ if (oldBatterySaverState == BatterySaverState.ON) {
+ mLastBatterySaverDisabledTime = now;
+ }
+ break;
}
}
@@ -377,7 +404,18 @@
pw.print(indent);
pw.print("Battery Saver is currently: ");
- pw.println(mIsBatterySaverEnabled ? "ON" : "OFF");
+ switch (BatterySaverState.fromIndex(mCurrentState)) {
+ case BatterySaverState.OFF:
+ pw.println("OFF");
+ break;
+ case BatterySaverState.ON:
+ pw.println("ON");
+ break;
+ case BatterySaverState.ADAPTIVE:
+ pw.println("ADAPTIVE");
+ break;
+ }
+
if (mLastBatterySaverEnabledTime > 0) {
pw.print(indent);
pw.print(" ");
@@ -400,9 +438,34 @@
pw.print(indent);
pw.print(" ");
- pw.print("Times enabled: ");
+ pw.print("Times full enabled: ");
pw.println(mBatterySaverEnabledCount);
+ if (mLastAdaptiveBatterySaverEnabledTime > 0) {
+ pw.print(indent);
+ pw.print(" ");
+ pw.print("Last ADAPTIVE ON time: ");
+ pw.print(sdf.format(
+ new Date(now - nowElapsed + mLastAdaptiveBatterySaverEnabledTime)));
+ pw.print(" ");
+ TimeUtils.formatDuration(mLastAdaptiveBatterySaverEnabledTime, nowElapsed, pw);
+ pw.println();
+ }
+ if (mLastAdaptiveBatterySaverDisabledTime > 0) {
+ pw.print(indent);
+ pw.print(" ");
+ pw.print("Last ADAPTIVE OFF time: ");
+ pw.print(sdf.format(
+ new Date(now - nowElapsed + mLastAdaptiveBatterySaverDisabledTime)));
+ pw.print(" ");
+ TimeUtils.formatDuration(mLastAdaptiveBatterySaverDisabledTime, nowElapsed, pw);
+ pw.println();
+ }
+ pw.print(indent);
+ pw.print(" ");
+ pw.print("Times adaptive enabled: ");
+ pw.println(mAdaptiveBatterySaverEnabledCount);
+
pw.println();
pw.print(indent);
@@ -436,8 +499,10 @@
pw.print(interactiveLabel);
pw.print(": ");
- final Stat offStat = getStat(BatterySaverState.OFF, interactiveState, dozeState);
- final Stat onStat = getStat(BatterySaverState.ON, interactiveState, dozeState);
+ final Stat offStat = getStat(BatterySaverState.OFF, interactiveState, dozeState,
+ PlugState.UNPLUGGED);
+ final Stat onStat = getStat(BatterySaverState.ON, interactiveState, dozeState,
+ PlugState.UNPLUGGED);
pw.println(String.format("%6dm %6dmAh(%3d%%) %8.1fmAh/h %6dm %6dmAh(%3d%%) %8.1fmAh/h",
offStat.totalMinutes(),
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 4f18d07..6ba675d 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -640,7 +640,7 @@
}
for (String apexPackageName : apexPackageNames) {
- // We will not recieve notifications when an apex is updated,
+ // We will not receive notifications when an apex is updated,
// so check now in case any rollbacks ought to be expired. The
// onPackagedReplace function is safe to call if the package
// hasn't actually been updated.
@@ -827,7 +827,7 @@
}
/**
- * Do code and userdata backups to enable rollback of the given session.
+ * Do code and user-data backups to enable rollback of the given session.
* In case of multiPackage sessions, <code>session</code> should be one of
* the child sessions, not the parent session.
*
@@ -915,7 +915,7 @@
}
}
- /**
+ /*
* The order is important here! Always enable the embedded apk-in-apex (if any) before
* enabling the embedding apex. Otherwise the rollback object might be in an inconsistent
* state where an embedding apex is successfully enabled while one of its embedded
@@ -1323,7 +1323,7 @@
assertInWorkerThread();
int rollbackId = allocateRollbackId();
final int userId;
- if (parentSession.getUser() == UserHandle.ALL) {
+ if (parentSession.getUser().equals(UserHandle.ALL)) {
userId = UserHandle.SYSTEM.getIdentifier();
} else {
userId = parentSession.getUser().getIdentifier();
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/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index d9415ce..3ec61fd 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -49,6 +49,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.Objects;
/**
@@ -188,20 +189,14 @@
int userId = UserHandle.getCallingUserId();
ConfigListenerInfo listenerInfo = new ConfigListenerInfo(userId, listener);
- final IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- synchronized (mConfigurationListeners) {
- Slog.i(TAG, "Configuration listener died: " + listenerInfo);
- mConfigurationListeners.remove(listenerInfo);
- }
- }
- };
synchronized (mConfigurationListeners) {
+ if (mConfigurationListeners.contains(listenerInfo)) {
+ return;
+ }
try {
- // Remove the record of the listener if the client process dies.
- listener.asBinder().linkToDeath(deathRecipient, 0 /* flags */);
+ // Ensure the reference to the listener is removed if the client process dies.
+ listenerInfo.linkToDeath();
// Only add the listener if we can linkToDeath().
mConfigurationListeners.add(listenerInfo);
@@ -211,6 +206,31 @@
}
}
+ @Override
+ public void removeConfigurationListener(@NonNull ITimeZoneConfigurationListener listener) {
+ enforceManageTimeZoneDetectorConfigurationPermission();
+ Objects.requireNonNull(listener);
+ int userId = UserHandle.getCallingUserId();
+
+ synchronized (mConfigurationListeners) {
+ ConfigListenerInfo toRemove = new ConfigListenerInfo(userId, listener);
+ Iterator<ConfigListenerInfo> listenerIterator = mConfigurationListeners.iterator();
+ while (listenerIterator.hasNext()) {
+ ConfigListenerInfo currentListenerInfo = listenerIterator.next();
+ if (currentListenerInfo.equals(toRemove)) {
+ listenerIterator.remove();
+
+ // Stop listening for the client process to die.
+ try {
+ currentListenerInfo.unlinkToDeath();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to unlinkToDeath() for listener=" + listener, e);
+ }
+ }
+ }
+ }
+ }
+
void handleConfigurationChanged() {
// Note: we could trigger an async time zone detection operation here via a call to
// handleAutoTimeZoneDetectionChanged(), but that is triggered in response to the underlying
@@ -319,7 +339,7 @@
this, in, out, err, args, callback, resultReceiver);
}
- private static class ConfigListenerInfo {
+ private class ConfigListenerInfo implements IBinder.DeathRecipient {
private final @UserIdInt int mUserId;
private final ITimeZoneConfigurationListener mListener;
@@ -337,6 +357,40 @@
return mListener;
}
+ void linkToDeath() throws RemoteException {
+ mListener.asBinder().linkToDeath(this, 0 /* flags */);
+ }
+
+ void unlinkToDeath() throws RemoteException {
+ mListener.asBinder().unlinkToDeath(this, 0 /* flags */);
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mConfigurationListeners) {
+ Slog.i(TAG, "Configuration listener client died: " + this);
+ mConfigurationListeners.remove(this);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ConfigListenerInfo that = (ConfigListenerInfo) o;
+ return mUserId == that.mUserId
+ && mListener.equals(that.mListener);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUserId, mListener);
+ }
+
@Override
public String toString() {
return "ConfigListenerInfo{"
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/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index b7bb924..dd67b3d2 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2952,8 +2952,8 @@
null /* resultData */, null /* resultGrants */);
makeFinishingLocked();
if (ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE) {
- Slog.i(TAG_ADD_REMOVE, "Removing activity " + this + " from stack callers="
- + Debug.getCallers(5));
+ Slog.i(TAG_ADD_REMOVE, "Removing activity " + this + " from stack, reason="
+ + reason + ", callers=" + Debug.getCallers(5));
}
takeFromHistory();
@@ -3655,13 +3655,6 @@
}
}
- @Override
- protected void reparentSurfaceControl(Transaction t, SurfaceControl newParent) {
- if (!mSurfaceAnimator.hasLeash()) {
- t.reparent(mSurfaceControl, newParent);
- }
- }
-
void logStartActivity(int tag, Task task) {
final Uri data = intent.getData();
final String strData = data != null ? data.toSafeString() : 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/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 751d0c8..e84f040 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1288,11 +1288,11 @@
return false;
}
// if the realCallingUid is a persistent system process, abort if the IntentSender
- // wasn't whitelisted to start an activity
+ // wasn't allowed to start an activity
if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
- + ") is persistent system process AND intent sender whitelisted "
+ + ") is persistent system process AND intent sender allowed "
+ "(allowBackgroundActivityStart = true)");
}
return false;
@@ -1346,23 +1346,23 @@
// If we don't have callerApp at this point, no caller was provided to startActivity().
// That's the case for PendingIntent-based starts, since the creator's process might not be
// up and alive. If that's the case, we retrieve the WindowProcessController for the send()
- // caller, so that we can make the decision based on its foreground/whitelisted state.
+ // caller, so that we can make the decision based on its state.
int callerAppUid = callingUid;
if (callerApp == null) {
callerApp = mService.getProcessController(realCallingPid, realCallingUid);
callerAppUid = realCallingUid;
}
- // don't abort if the callerApp or other processes of that uid are whitelisted in any way
+ // don't abort if the callerApp or other processes of that uid are allowed in any way
if (callerApp != null) {
// first check the original calling process
if (callerApp.areBackgroundActivityStartsAllowed()) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Background activity start allowed: callerApp process (pid = "
- + callerApp.getPid() + ", uid = " + callerAppUid + ") is whitelisted");
+ + callerApp.getPid() + ", uid = " + callerAppUid + ") is allowed");
}
return false;
}
- // only if that one wasn't whitelisted, check the other ones
+ // only if that one wasn't allowed, check the other ones
final ArraySet<WindowProcessController> uidProcesses =
mService.mProcessMap.getProcesses(callerAppUid);
if (uidProcesses != null) {
@@ -1372,7 +1372,7 @@
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG,
"Background activity start allowed: process " + proc.getPid()
- + " from uid " + callerAppUid + " is whitelisted");
+ + " from uid " + callerAppUid + " is allowed");
}
return false;
}
@@ -1401,7 +1401,7 @@
+ "; isRealCallingUidPersistentSystemProcess: "
+ isRealCallingUidPersistentSystemProcess
+ "; originatingPendingIntent: " + originatingPendingIntent
- + "; isBgStartWhitelisted: " + allowBackgroundActivityStart
+ + "; allowBackgroundActivityStart: " + allowBackgroundActivityStart
+ "; intent: " + intent
+ "; callerApp: " + callerApp
+ "]");
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index d5df906..777ddda 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -125,20 +125,30 @@
* Sleep tokens cause the activity manager to put the top activity to sleep.
* They are used by components such as dreams that may hide and block interaction
* with underlying activities.
+ * The Acquirer provides an interface that encapsulates the underlying work, so the user does
+ * not need to handle the token by him/herself.
*/
- public static abstract class SleepToken {
+ public interface SleepTokenAcquirer {
- /** Releases the sleep token. */
- public abstract void release();
+ /**
+ * Acquires a sleep token.
+ * @param displayId The display to apply to.
+ */
+ void acquire(int displayId);
+
+ /**
+ * Releases the sleep token.
+ * @param displayId The display to apply to.
+ */
+ void release(int displayId);
}
/**
- * Acquires a sleep token for the specified display with the specified tag.
+ * Creates a sleep token acquirer for the specified display with the specified tag.
*
- * @param tag A string identifying the purpose of the token (eg. "Dream").
- * @param displayId The display to apply the sleep token to.
+ * @param tag A string identifying the purpose (eg. "Dream").
*/
- public abstract SleepToken acquireSleepToken(@NonNull String tag, int displayId);
+ public abstract SleepTokenAcquirer createSleepTokenAcquirer(@NonNull String tag);
/**
* Returns home activity for the specified user.
@@ -211,6 +221,14 @@
boolean allowBackgroundActivityStart);
/**
+ * Callback to be called on certain activity start scenarios.
+ *
+ * @see BackgroundActivityStartCallback
+ */
+ public abstract void setBackgroundActivityStartCallback(
+ @Nullable BackgroundActivityStartCallback callback);
+
+ /**
* Start activity {@code intent} without calling user-id check.
*
* - DO NOT call it with the calling UID cleared.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 5dc0a10..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;
@@ -675,6 +674,9 @@
WindowOrganizerController mWindowOrganizerController;
TaskOrganizerController mTaskOrganizerController;
+ @Nullable
+ private BackgroundActivityStartCallback mBackgroundActivityStartCallback;
+
private int mDeviceOwnerUid = Process.INVALID_UID;
private final class FontScaleSettingObserver extends ContentObserver {
@@ -1001,6 +1003,11 @@
return config;
}
+ @Nullable
+ public BackgroundActivityStartCallback getBackgroundActivityStartCallback() {
+ return mBackgroundActivityStartCallback;
+ }
+
private void start() {
LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
}
@@ -2464,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) {
@@ -2478,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 */);
}
}
}
@@ -2835,7 +2837,7 @@
}
if (toTop) {
- taskDisplayArea.positionStackAt(POSITION_TOP, primarySplitTask,
+ taskDisplayArea.positionChildAt(POSITION_TOP, primarySplitTask,
false /* includingParents */);
}
WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -5070,7 +5072,10 @@
final long sleepToken = proto.start(ActivityManagerServiceDumpProcessesProto.SLEEP_STATUS);
proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.WAKEFULNESS,
PowerManagerInternal.wakefulnessToProtoEnum(wakeFullness));
- for (ActivityTaskManagerInternal.SleepToken st : mRootWindowContainer.mSleepTokens) {
+ final int tokenSize = mRootWindowContainer.mSleepTokens.size();
+ for (int i = 0; i < tokenSize; i++) {
+ final RootWindowContainer.SleepToken st =
+ mRootWindowContainer.mSleepTokens.valueAt(i);
proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEP_TOKENS,
st.toString());
}
@@ -5504,12 +5509,35 @@
reason);
}
- ActivityTaskManagerInternal.SleepToken acquireSleepToken(String tag, int displayId) {
- synchronized (mGlobalLock) {
- final ActivityTaskManagerInternal.SleepToken token =
- mRootWindowContainer.createSleepToken(tag, displayId);
- updateSleepIfNeededLocked();
- return token;
+ final class SleepTokenAcquirerImpl implements ActivityTaskManagerInternal.SleepTokenAcquirer {
+ private final String mTag;
+ private final SparseArray<RootWindowContainer.SleepToken> mSleepTokens =
+ new SparseArray<>();
+
+ SleepTokenAcquirerImpl(@NonNull String tag) {
+ mTag = tag;
+ }
+
+ @Override
+ public void acquire(int displayId) {
+ synchronized (mGlobalLock) {
+ if (!mSleepTokens.contains(displayId)) {
+ mSleepTokens.append(displayId,
+ mRootWindowContainer.createSleepToken(mTag, displayId));
+ updateSleepIfNeededLocked();
+ }
+ }
+ }
+
+ @Override
+ public void release(int displayId) {
+ synchronized (mGlobalLock) {
+ final RootWindowContainer.SleepToken token = mSleepTokens.get(displayId);
+ if (token != null) {
+ mRootWindowContainer.removeSleepToken(token);
+ mSleepTokens.remove(displayId);
+ }
+ }
}
}
@@ -6068,9 +6096,9 @@
final class LocalService extends ActivityTaskManagerInternal {
@Override
- public SleepToken acquireSleepToken(String tag, int displayId) {
+ public SleepTokenAcquirer createSleepTokenAcquirer(@NonNull String tag) {
Objects.requireNonNull(tag);
- return ActivityTaskManagerService.this.acquireSleepToken(tag, displayId);
+ return new SleepTokenAcquirerImpl(tag);
}
@Override
@@ -6116,6 +6144,11 @@
return false;
}
+ public void setBackgroundActivityStartCallback(
+ @Nullable BackgroundActivityStartCallback backgroundActivityStartCallback) {
+ mBackgroundActivityStartCallback = backgroundActivityStartCallback;
+ }
+
@Override
public int startActivitiesAsPackage(String packageName, @Nullable String featureId,
int userId, Intent[] intents, Bundle bOptions) {
@@ -6820,7 +6853,8 @@
synchronized (mGlobalLock) {
// Clean-up disabled activities.
if (mRootWindowContainer.finishDisabledPackageActivities(
- packageName, disabledClasses, true, false, userId) && booted) {
+ packageName, disabledClasses, true /* doit */, false /* evenPersistent */,
+ userId, false /* onlyRemoveNoProcess */) && booted) {
mRootWindowContainer.resumeFocusedStacksTopActivities();
mStackSupervisor.scheduleIdle();
}
@@ -6839,7 +6873,11 @@
boolean didSomething =
getActivityStartController().clearPendingActivityLaunches(packageName);
didSomething |= mRootWindowContainer.finishDisabledPackageActivities(packageName,
- null, doit, evenPersistent, userId);
+ null /* filterByClasses */, doit, evenPersistent, userId,
+ // Only remove the activities without process because the activities with
+ // attached process will be removed when handling process died with
+ // WindowProcessController#isRemoved == true.
+ true /* onlyRemoveNoProcess */);
return didSomething;
}
}
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java b/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java
new file mode 100644
index 0000000..4e742b9
--- /dev/null
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.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.server.wm;
+
+import android.os.IBinder;
+
+/**
+ * Callback to be called when a background activity start is allowed exclusively because of the
+ * token provided in {@link #getToken()}.
+ */
+public interface BackgroundActivityStartCallback {
+ /**
+ * The token that allowed the activity start that triggered {@link
+ * #onExclusiveTokenActivityStart()}.
+ *
+ * Ideally this should just return a final variable, don't do anything costly here (don't hold
+ * any locks).
+ */
+ IBinder getToken();
+
+ /**
+ * Called when the background activity start happens.
+ */
+ void onExclusiveTokenActivityStart(String packageName);
+}
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/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
index 33eb3ce..be18d0a 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -37,18 +37,19 @@
import java.util.List;
/**
- * Policy that manages DisplayAreas.
+ * Policy that manages {@link DisplayArea}.
*/
public abstract class DisplayAreaPolicy {
protected final WindowManagerService mWmService;
/**
- * The root DisplayArea. Attach all DisplayAreas to this area (directly or indirectly).
+ * The {@link RootDisplayArea} of the whole logical display. All {@link DisplayArea}s must be
+ * (direct or indirect) descendants of this area.
*/
protected final RootDisplayArea mRoot;
/**
- * Construct a new {@link DisplayAreaPolicy}
+ * Constructs a new {@link DisplayAreaPolicy}
*
* @param wmService the window manager service instance
* @param root the root display area under which the policy operates
@@ -59,9 +60,10 @@
}
/**
- * Called to ask the policy to attach the given WindowToken to the DisplayArea hierarchy.
+ * Called to ask the policy to attach the given {@link WindowToken} to the {@link DisplayArea}
+ * hierarchy.
*
- * This must attach the token to mRoot (or one of its descendants).
+ * <p>This must attach the token to {@link #mRoot} (or one of its descendants).
*/
public abstract void addWindow(WindowToken token);
@@ -121,13 +123,14 @@
/**
* Provider for {@link DisplayAreaPolicy} instances.
*
- * By implementing this interface and overriding the
+ * <p>By implementing this interface and overriding the
* {@code config_deviceSpecificDisplayAreaPolicyProvider}, a device-specific implementations
* of {@link DisplayAreaPolicy} can be supplied.
*/
public interface Provider {
/**
- * Instantiates a new DisplayAreaPolicy. It should set up the {@link DisplayArea} hierarchy.
+ * Instantiates a new {@link DisplayAreaPolicy}. It should set up the {@link DisplayArea}
+ * hierarchy.
*
* @see DisplayAreaPolicy#DisplayAreaPolicy
*/
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
index 681b21c..b6cff62 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
@@ -46,31 +46,90 @@
* A builder for instantiating a complex {@link DisplayAreaPolicy}
*
* <p>Given a set of features (that each target a set of window types), it builds the necessary
- * DisplayArea hierarchy.
+ * {@link DisplayArea} hierarchy.
*
- * <p>Example: <br />
+ * <p>Example:
*
- * <pre>
- * // Feature for targeting everything below the magnification overlay:
- * new DisplayAreaPolicyBuilder(...)
- * .addFeature(new Feature.Builder(..., "Magnification")
- * .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
- * .build())
- * .build(...)
+ * <pre class="prettyprint">
+ * // Build root hierarchy of the logical display.
+ * DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy =
+ * new DisplayAreaPolicyBuilder.HierarchyBuilder(root)
+ * // Feature for targeting everything below the magnification overlay
+ * .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder(wmService.mPolicy,
+ * "WindowedMagnification", FEATURE_WINDOWED_MAGNIFICATION)
+ * .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
+ * .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
+ * // Make the DA dimmable so that the magnify window also mirrors the
+ * // dim layer
+ * .setNewDisplayAreaSupplier(DisplayArea.Dimmable::new)
+ * .build())
+ * .setImeContainer(imeContainer)
+ * .setTaskDisplayAreas(rootTdaList);
*
- * // Builds a policy with the following hierarchy:
- * - RootDisplayArea
- * - Magnification
- * - DisplayArea.Tokens (Wallpapers are attached here)
- * - TaskDisplayArea
- * - DisplayArea.Tokens (windows above Tasks up to IME are attached here)
- * - ImeContainers
- * - DisplayArea.Tokens (windows above IME up to TYPE_ACCESSIBILITY_OVERLAY attached here)
- * - DisplayArea.Tokens (TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY and up are attached here)
+ * // Build root hierarchy of front and rear DisplayAreaGroup.
+ * RootDisplayArea frontRoot = new RootDisplayArea(wmService, "FrontRoot", FEATURE_FRONT_ROOT);
+ * DisplayAreaPolicyBuilder.HierarchyBuilder frontGroupHierarchy =
+ * new DisplayAreaPolicyBuilder.HierarchyBuilder(frontRoot)
+ * // (Optional) .addFeature(...)
+ * .setTaskDisplayAreas(frontTdaList);
*
+ * RootDisplayArea rearRoot = new RootDisplayArea(wmService, "RearRoot", FEATURE_REAR_ROOT);
+ * DisplayAreaPolicyBuilder.HierarchyBuilder rearGroupHierarchy =
+ * new DisplayAreaPolicyBuilder.HierarchyBuilder(rearRoot)
+ * // (Optional) .addFeature(...)
+ * .setTaskDisplayAreas(rearTdaList);
+ *
+ * // Define the function to select root for window to attach.
+ * BiFunction<WindowToken, Bundle, RootDisplayArea> selectRootForWindowFunc =
+ * (windowToken, bundle) -> {
+ * if (bundle == null) {
+ * return root;
+ * }
+ * // OEMs need to define the condition.
+ * if (...) {
+ * return frontRoot;
+ * }
+ * if (...) {
+ * return rearRoot;
+ * }
+ * return root;
+ * };
+ *
+ * return new DisplayAreaPolicyBuilder()
+ * .setRootHierarchy(rootHierarchy)
+ * .addDisplayAreaGroupHierarchy(frontGroupHierarchy)
+ * .addDisplayAreaGroupHierarchy(rearGroupHierarchy)
+ * .setSelectRootForWindowFunc(selectRootForWindowFunc)
+ * .build(wmService, content);
* </pre>
*
- * // TODO(b/158713595): document more complex scenarios where we need multiple areas per feature.
+ * This builds a policy with the following hierarchy:
+ * <pre class="prettyprint">
+ * - RootDisplayArea (DisplayContent)
+ * - WindowedMagnification
+ * - DisplayArea.Tokens (Wallpapers can be attached here)
+ * - TaskDisplayArea
+ * - RootDisplayArea (FrontRoot)
+ * - DisplayArea.Tokens (Wallpapers can be attached here)
+ * - TaskDisplayArea
+ * - DisplayArea.Tokens (windows above Tasks up to IME can be attached here)
+ * - DisplayArea.Tokens (windows above IME can be attached here)
+ * - RootDisplayArea (RearRoot)
+ * - DisplayArea.Tokens (Wallpapers can be attached here)
+ * - TaskDisplayArea
+ * - DisplayArea.Tokens (windows above Tasks up to IME can be attached here)
+ * - DisplayArea.Tokens (windows above IME can be attached here)
+ * - DisplayArea.Tokens (windows above Tasks up to IME can be attached here)
+ * - ImeContainers
+ * - DisplayArea.Tokens (windows above IME up to TYPE_ACCESSIBILITY_OVERLAY can be
+ * attached here)
+ * - DisplayArea.Tokens (TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY and up can be attached
+ * here)
+ * </pre>
+ * When a {@link WindowToken} of Wallpaper needs to be attached, the policy will call the OEM
+ * defined {@link #mSelectRootForWindowFunc} to get a {@link RootDisplayArea}. It will then place
+ * the window to the corresponding {@link DisplayArea.Tokens} under the returned root
+ * {@link RootDisplayArea}.
*/
class DisplayAreaPolicyBuilder {
@Nullable private HierarchyBuilder mRootHierarchyBuilder;
@@ -396,7 +455,7 @@
/**
* Returns the id of the feature.
*
- * Must be unique among the features added to a {@link DisplayAreaPolicyBuilder}.
+ * <p>Must be unique among the features added to a {@link DisplayAreaPolicyBuilder}.
*
* @see android.window.DisplayAreaOrganizer#FEATURE_SYSTEM_FIRST
* @see android.window.DisplayAreaOrganizer#FEATURE_VENDOR_FIRST
@@ -425,7 +484,7 @@
* For example, {@code all().except(TYPE_STATUS_BAR)} expresses that a feature should
* apply to all types except TYPE_STATUS_BAR.
*
- * The builder starts out with the feature not applying to any types.
+ * <p>The builder starts out with the feature not applying to any types.
*
* @param name the name of the feature.
* @param id of the feature. {@see Feature#getId}
@@ -599,7 +658,7 @@
* Whether to skip creating a {@link DisplayArea.Tokens} if {@link #mExisting} is
* {@code null}.
*
- * This will be set for {@link HierarchyBuilder#LEAF_TYPE_IME_CONTAINERS} and
+ * <p>This will be set for {@link HierarchyBuilder#LEAF_TYPE_IME_CONTAINERS} and
* {@link HierarchyBuilder#LEAF_TYPE_TASK_CONTAINERS}, because we don't want to create
* {@link DisplayArea.Tokens} for them even if they are not set.
*/
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 936dbdf..0b1f4d9e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -490,6 +490,8 @@
*/
private ActivityRecord mFixedRotationLaunchingApp;
+ /** The delay to avoid toggling the animation quickly. */
+ private static final long FIXED_ROTATION_HIDE_ANIMATION_DEBOUNCE_DELAY_MS = 250;
private FixedRotationAnimationController mFixedRotationAnimationController;
final FixedRotationTransitionListener mFixedRotationTransitionListener =
@@ -585,9 +587,9 @@
private IntArray mDisplayAccessUIDs = new IntArray();
/** All tokens used to put activities on this stack to sleep (including mOffToken) */
- final ArrayList<ActivityTaskManagerInternal.SleepToken> mAllSleepTokens = new ArrayList<>();
- /** The token acquired by ActivityStackSupervisor to put stacks on the display to sleep */
- ActivityTaskManagerInternal.SleepToken mOffToken;
+ final ArrayList<RootWindowContainer.SleepToken> mAllSleepTokens = new ArrayList<>();
+ /** The token acquirer to put stacks on the display to sleep */
+ private final ActivityTaskManagerInternal.SleepTokenAcquirer mOffTokenAcquirer;
private boolean mSleeping;
@@ -923,6 +925,7 @@
mAtmService = mWmService.mAtmService;
mDisplay = display;
mDisplayId = display.getDisplayId();
+ mOffTokenAcquirer = mRootWindowContainer.mDisplayOffTokenAcquirer;
mWallpaperController = new WallpaperController(mWmService, this);
display.getDisplayInfo(mDisplayInfo);
display.getMetrics(mDisplayMetrics);
@@ -1523,10 +1526,10 @@
void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r, int rotation) {
if (mFixedRotationLaunchingApp == null && r != null) {
mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this, rotation);
- if (mFixedRotationAnimationController == null) {
- mFixedRotationAnimationController = new FixedRotationAnimationController(this);
- mFixedRotationAnimationController.hide();
- }
+ startFixedRotationAnimation(
+ // Delay the hide animation to avoid blinking by clicking navigation bar that
+ // may toggle fixed rotation in a short time.
+ r == mFixedRotationTransitionListener.mAnimatingRecents /* shouldDebounce */);
} else if (mFixedRotationLaunchingApp != null && r == null) {
mWmService.mDisplayNotificationController.dispatchFixedRotationFinished(this);
finishFixedRotationAnimationIfPossible();
@@ -1624,6 +1627,32 @@
}
}
+ /**
+ * Starts the hide animation for the windows which will be rotated seamlessly.
+ *
+ * @return {@code true} if the animation is executed right now.
+ */
+ private boolean startFixedRotationAnimation(boolean shouldDebounce) {
+ if (shouldDebounce) {
+ mWmService.mH.postDelayed(() -> {
+ synchronized (mWmService.mGlobalLock) {
+ if (mFixedRotationLaunchingApp != null
+ && startFixedRotationAnimation(false /* shouldDebounce */)) {
+ // Apply the transaction so the animation leash can take effect immediately.
+ getPendingTransaction().apply();
+ }
+ }
+ }, FIXED_ROTATION_HIDE_ANIMATION_DEBOUNCE_DELAY_MS);
+ return false;
+ }
+ if (mFixedRotationAnimationController == null) {
+ mFixedRotationAnimationController = new FixedRotationAnimationController(this);
+ mFixedRotationAnimationController.hide();
+ return true;
+ }
+ return false;
+ }
+
/** Re-show the previously hidden windows if all seamless rotated windows are done. */
void finishFixedRotationAnimationIfPossible() {
final FixedRotationAnimationController controller = mFixedRotationAnimationController;
@@ -3527,18 +3556,6 @@
== mRemoteInsetsControlTarget)) {
return mRemoteInsetsControlTarget;
} else {
- // Now, a special case -- if the last target's window is in the process of exiting, but
- // not removed, keep on the last target to avoid IME flicker.
- final WindowState cur = InsetsControlTarget.asWindowOrNull(mInputMethodControlTarget);
- if (cur != null && !cur.mRemoved && cur.isDisplayedLw() && cur.isClosing()
- && !cur.isActivityTypeHome()) {
- if (DEBUG_INPUT_METHOD) {
- Slog.v(TAG_WM, "Not changing control while current window"
- + " is closing and not removed");
- }
- return cur;
- }
- // Otherwise, we just use the ime target as received from IME.
return mInputMethodInputTarget;
}
}
@@ -4931,11 +4948,10 @@
final int displayId = mDisplay.getDisplayId();
if (displayId != DEFAULT_DISPLAY) {
final int displayState = mDisplay.getState();
- if (displayState == Display.STATE_OFF && mOffToken == null) {
- mOffToken = mAtmService.acquireSleepToken("Display-off", displayId);
- } else if (displayState == Display.STATE_ON && mOffToken != null) {
- mOffToken.release();
- mOffToken = null;
+ if (displayState == Display.STATE_OFF) {
+ mOffTokenAcquirer.acquire(mDisplayId);
+ } else if (displayState == Display.STATE_ON) {
+ mOffTokenAcquirer.release(mDisplayId);
}
}
mWmService.requestTraversal();
@@ -5175,7 +5191,8 @@
mDisplayPolicy.release();
if (!mAllSleepTokens.isEmpty()) {
- mRootWindowContainer.mSleepTokens.removeAll(mAllSleepTokens);
+ mAllSleepTokens.forEach(token ->
+ mRootWindowContainer.mSleepTokens.remove(token.mHashKey));
mAllSleepTokens.clear();
mAtmService.updateSleepIfNeededLocked();
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 8322051..4f6f75d 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -144,7 +144,7 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.input.InputManager;
-import android.hardware.power.V1_0.PowerHint;
+import android.hardware.power.Boost;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -569,8 +569,8 @@
@Override
public void onFling(int duration) {
if (mService.mPowerManagerInternal != null) {
- mService.mPowerManagerInternal.powerHint(
- PowerHint.INTERACTION, duration);
+ mService.mPowerManagerInternal.setPowerBoost(
+ Boost.INTERACTION, duration);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 0747e24..c63128c 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -42,7 +42,7 @@
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
-import android.hardware.power.V1_0.PowerHint;
+import android.hardware.power.Boost;
import android.net.Uri;
import android.os.Handler;
import android.os.RemoteException;
@@ -1473,7 +1473,7 @@
@Override
public void run() {
// Send interaction power boost to improve redraw performance.
- mService.mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
+ mService.mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0);
if (isRotationChoicePossible(mCurrentAppOrientation)) {
final boolean isValid = isValidRotationChoice(mRotation);
sendProposedRotationChangeToStatusBarInternal(mRotation, isValid);
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/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index aea36d2..c36a75b 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -50,7 +50,6 @@
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
import java.io.PrintWriter;
@@ -74,11 +73,14 @@
private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>();
private final ActivityTaskManagerService mService;
private RootWindowContainer mRootWindowContainer;
+ private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer;
+
KeyguardController(ActivityTaskManagerService service,
ActivityStackSupervisor stackSupervisor) {
mService = service;
mStackSupervisor = stackSupervisor;
+ mSleepTokenAcquirer = mService.new SleepTokenAcquirerImpl("keyguard");
}
void setWindowManager(WindowManagerService windowManager) {
@@ -412,17 +414,17 @@
private void updateKeyguardSleepToken(int displayId) {
final KeyguardDisplayState state = getDisplay(displayId);
- if (isKeyguardUnoccludedOrAodShowing(displayId) && state.mSleepToken == null) {
- state.acquiredSleepToken();
- } else if (!isKeyguardUnoccludedOrAodShowing(displayId) && state.mSleepToken != null) {
- state.releaseSleepToken();
+ if (isKeyguardUnoccludedOrAodShowing(displayId)) {
+ state.mSleepTokenAcquirer.acquire(displayId);
+ } else if (!isKeyguardUnoccludedOrAodShowing(displayId)) {
+ state.mSleepTokenAcquirer.release(displayId);
}
}
private KeyguardDisplayState getDisplay(int displayId) {
KeyguardDisplayState state = mDisplayStates.get(displayId);
if (state == null) {
- state = new KeyguardDisplayState(mService, displayId);
+ state = new KeyguardDisplayState(mService, displayId, mSleepTokenAcquirer);
mDisplayStates.append(displayId, state);
}
return state;
@@ -443,29 +445,18 @@
private ActivityRecord mDismissingKeyguardActivity;
private boolean mRequestDismissKeyguard;
private final ActivityTaskManagerService mService;
- private SleepToken mSleepToken;
+ private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer;
- KeyguardDisplayState(ActivityTaskManagerService service, int displayId) {
+ KeyguardDisplayState(ActivityTaskManagerService service, int displayId,
+ ActivityTaskManagerInternal.SleepTokenAcquirer acquirer) {
mService = service;
mDisplayId = displayId;
+ mSleepTokenAcquirer = acquirer;
}
void onRemoved() {
mDismissingKeyguardActivity = null;
- releaseSleepToken();
- }
-
- void acquiredSleepToken() {
- if (mSleepToken == null) {
- mSleepToken = mService.acquireSleepToken("keyguard", mDisplayId);
- }
- }
-
- void releaseSleepToken() {
- if (mSleepToken != null) {
- mSleepToken.release();
- mSleepToken = null;
- }
+ mSleepTokenAcquirer.release(mDisplayId);
}
void visibilitiesUpdated(KeyguardController controller, DisplayContent display) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 4241968..06dec7c 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -112,7 +112,7 @@
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
-import android.hardware.power.V1_0.PowerHint;
+import android.hardware.power.Mode;
import android.net.Uri;
import android.os.Binder;
import android.os.Debug;
@@ -220,6 +220,9 @@
// transaction from the global transaction.
private final SurfaceControl.Transaction mDisplayTransaction;
+ /** The token acquirer to put stacks on the displays to sleep */
+ final ActivityTaskManagerInternal.SleepTokenAcquirer mDisplayOffTokenAcquirer;
+
/**
* The modes which affect which tasks are returned when calling
* {@link RootWindowContainer#anyTaskForId(int)}.
@@ -259,7 +262,7 @@
* They are used by components that may hide and block interaction with underlying
* activities.
*/
- final ArrayList<ActivityTaskManagerInternal.SleepToken> mSleepTokens = new ArrayList<>();
+ final SparseArray<SleepToken> mSleepTokens = new SparseArray<>();
/** Set when a power mode launch has started, but not ended. */
private boolean mPowerModeLaunchStarted;
@@ -444,6 +447,7 @@
mService = service.mAtmService;
mStackSupervisor = mService.mStackSupervisor;
mStackSupervisor.mRootWindowContainer = this;
+ mDisplayOffTokenAcquirer = mService.new SleepTokenAcquirerImpl("Display-off");
}
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
@@ -951,9 +955,9 @@
if (mSustainedPerformanceModeCurrent != mSustainedPerformanceModeEnabled) {
mSustainedPerformanceModeEnabled = mSustainedPerformanceModeCurrent;
- mWmService.mPowerManagerInternal.powerHint(
- PowerHint.SUSTAINED_PERFORMANCE,
- mSustainedPerformanceModeEnabled ? 1 : 0);
+ mWmService.mPowerManagerInternal.setPowerMode(
+ Mode.SUSTAINED_PERFORMANCE,
+ mSustainedPerformanceModeEnabled);
}
if (mUpdateRotation) {
@@ -2619,20 +2623,29 @@
}
}
- ActivityTaskManagerInternal.SleepToken createSleepToken(String tag, int displayId) {
+ SleepToken createSleepToken(String tag, int displayId) {
final DisplayContent display = getDisplayContent(displayId);
if (display == null) {
throw new IllegalArgumentException("Invalid display: " + displayId);
}
- final SleepTokenImpl token = new SleepTokenImpl(tag, displayId);
- mSleepTokens.add(token);
- display.mAllSleepTokens.add(token);
+ final int tokenKey = makeSleepTokenKey(tag, displayId);
+ SleepToken token = mSleepTokens.get(tokenKey);
+ if (token == null) {
+ token = new SleepToken(tag, displayId);
+ mSleepTokens.put(tokenKey, token);
+ display.mAllSleepTokens.add(token);
+ } else {
+ throw new RuntimeException("Create the same sleep token twice: " + token);
+ }
return token;
}
- private void removeSleepToken(SleepTokenImpl token) {
- mSleepTokens.remove(token);
+ void removeSleepToken(SleepToken token) {
+ if (!mSleepTokens.contains(token.mHashKey)) {
+ Slog.d(TAG, "Remove non-exist sleep token: " + token + " from " + Debug.getCallers(6));
+ }
+ mSleepTokens.remove(token.mHashKey);
final DisplayContent display = getDisplayContent(token.mDisplayId);
if (display != null) {
@@ -3092,6 +3105,12 @@
}
boolean handleAppDied(WindowProcessController app) {
+ if (app.isRemoved()) {
+ // The package of the died process should be force-stopped, so make its activities as
+ // finishing to prevent the process from being started again if the next top (or being
+ // visible) activity also resides in the same process.
+ app.makeFinishingForProcessRemoved();
+ }
return reduceOnAllTaskDisplayAreas((taskDisplayArea, result) -> {
for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
final Task stack = taskDisplayArea.getStackAt(sNdx);
@@ -3129,24 +3148,26 @@
private boolean mDoit;
private boolean mEvenPersistent;
private int mUserId;
+ private boolean mOnlyRemoveNoProcess;
private Task mLastTask;
private ComponentName mHomeActivity;
private void reset(String packageName, Set<String> filterByClasses,
- boolean doit, boolean evenPersistent, int userId) {
+ boolean doit, boolean evenPersistent, int userId, boolean onlyRemoveNoProcess) {
mDidSomething = false;
mPackageName = packageName;
mFilterByClasses = filterByClasses;
mDoit = doit;
mEvenPersistent = evenPersistent;
mUserId = userId;
+ mOnlyRemoveNoProcess = onlyRemoveNoProcess;
mLastTask = null;
mHomeActivity = null;
}
boolean process(String packageName, Set<String> filterByClasses,
- boolean doit, boolean evenPersistent, int userId) {
- reset(packageName, filterByClasses, doit, evenPersistent, userId);
+ boolean doit, boolean evenPersistent, int userId, boolean onlyRemoveNoProcess) {
+ reset(packageName, filterByClasses, doit, evenPersistent, userId, onlyRemoveNoProcess);
final PooledFunction f = PooledLambda.obtainFunction(
FinishDisabledPackageActivitiesHelper::processActivity, this,
@@ -3161,9 +3182,10 @@
(r.packageName.equals(mPackageName) && (mFilterByClasses == null
|| mFilterByClasses.contains(r.mActivityComponent.getClassName())))
|| (mPackageName == null && r.mUserId == mUserId);
+ final boolean noProcess = !r.hasProcess();
if ((mUserId == UserHandle.USER_ALL || r.mUserId == mUserId)
&& (sameComponent || r.getTask() == mLastTask)
- && (r.app == null || mEvenPersistent || !r.app.isPersistent())) {
+ && (noProcess || mEvenPersistent || !r.app.isPersistent())) {
if (!mDoit) {
if (r.finishing) {
// If this activity is just finishing, then it is not
@@ -3180,10 +3202,19 @@
mHomeActivity = r.mActivityComponent;
}
}
- mDidSomething = true;
- Slog.i(TAG, " Force finishing activity " + r);
+ if (mOnlyRemoveNoProcess) {
+ if (noProcess) {
+ mDidSomething = true;
+ Slog.i(TAG, " Force removing " + r);
+ r.cleanUp(false /* cleanServices */, false /* setState */);
+ r.removeFromHistory("force-stop");
+ }
+ } else {
+ mDidSomething = true;
+ Slog.i(TAG, " Force finishing " + r);
+ r.finishIfPossible("force-stop", true /* oomAdj */);
+ }
mLastTask = r.getTask();
- r.finishIfPossible("force-stop", true);
}
return false;
@@ -3192,9 +3223,9 @@
/** @return true if some activity was finished (or would have finished if doit were true). */
boolean finishDisabledPackageActivities(String packageName, Set<String> filterByClasses,
- boolean doit, boolean evenPersistent, int userId) {
+ boolean doit, boolean evenPersistent, int userId, boolean onlyRemoveNoProcess) {
return mFinishDisabledPackageActivitiesHelper.process(packageName, filterByClasses, doit,
- evenPersistent, userId);
+ evenPersistent, userId, onlyRemoveNoProcess);
}
void updateActivityApplicationInfo(ApplicationInfo aInfo) {
@@ -3497,7 +3528,7 @@
}
if (sendPowerModeLaunch && mService.mPowerManagerInternal != null) {
- mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 1);
+ mService.mPowerManagerInternal.setPowerMode(Mode.LAUNCH, true);
mPowerModeLaunchStarted = true;
}
}
@@ -3505,7 +3536,7 @@
void endPowerModeLaunchIfNeeded() {
// Trigger launch power mode off if activity is launched
if (mPowerModeLaunchStarted && mService.mPowerManagerInternal != null) {
- mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 0);
+ mService.mPowerManagerInternal.setPowerMode(Mode.LAUNCH, false);
mPowerModeLaunchStarted = false;
}
}
@@ -3613,22 +3644,22 @@
return printed[0];
}
- private final class SleepTokenImpl extends ActivityTaskManagerInternal.SleepToken {
+ private static int makeSleepTokenKey(String tag, int displayId) {
+ final String tokenKey = tag + displayId;
+ return tokenKey.hashCode();
+ }
+
+ static final class SleepToken {
private final String mTag;
private final long mAcquireTime;
private final int mDisplayId;
+ final int mHashKey;
- public SleepTokenImpl(String tag, int displayId) {
+ SleepToken(String tag, int displayId) {
mTag = tag;
mDisplayId = displayId;
mAcquireTime = SystemClock.uptimeMillis();
- }
-
- @Override
- public void release() {
- synchronized (mService.mGlobalLock) {
- removeSleepToken(this);
- }
+ mHashKey = makeSleepTokenKey(mTag, mDisplayId);
}
@Override
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 837f1b5..34d084a 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -26,7 +26,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
-import android.hardware.power.V1_0.PowerHint;
+import android.hardware.power.Boost;
import android.os.Handler;
import android.os.PowerManagerInternal;
import android.util.ArrayMap;
@@ -246,7 +246,7 @@
synchronized (mLock) {
startPendingAnimationsLocked();
}
- mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
+ mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0);
}
private void scheduleApplyTransaction() {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 83fff26..da1e426 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -771,12 +771,6 @@
mHasVisibleActivities = false;
mApp = app;
mIsProcessRemoved = app.isRemoved();
- if (mIsProcessRemoved) {
- // The package of the died process should be force-stopped, so make its activities
- // as finishing to prevent the process from being started again if the next top
- // (or being visible) activity also resides in the same process.
- app.makeFinishingForProcessRemoved();
- }
final PooledConsumer c = PooledLambda.obtainConsumer(
RemoveHistoryRecordsForApp::addActivityToRemove, this,
@@ -2390,6 +2384,15 @@
saveLaunchingStateIfNeeded();
final boolean taskOrgChanged = updateTaskOrganizerState(false /* forceUpdate */);
+ if (taskOrgChanged) {
+ updateSurfacePosition(getSyncTransaction());
+ if (!isOrganized()) {
+ // Surface-size update was skipped before (since internally it no-ops if
+ // isOrganized() is true); however, now that this is not organized, the surface
+ // size needs to be updated by WM.
+ updateSurfaceSize(getSyncTransaction());
+ }
+ }
// If the task organizer has changed, then it will already be receiving taskAppeared with
// the latest task-info thus the task-info won't have changed.
if (!taskOrgChanged && isOrganized()) {
@@ -2465,7 +2468,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 */);
}
}
@@ -3373,7 +3376,7 @@
final int boundsChange = super.setBounds(bounds);
mRotation = rotation;
- updateSurfacePosition();
+ updateSurfacePositionNonOrganized();
return boundsChange;
}
@@ -3749,6 +3752,12 @@
}
@Override
+ void resetSurfacePositionForAnimationLeash(SurfaceControl.Transaction t) {
+ if (isOrganized()) return;
+ super.resetSurfacePositionForAnimationLeash(t);
+ }
+
+ @Override
Rect getAnimationBounds(int appStackClipMode) {
// TODO(b/131661052): we should remove appStackClipMode with hierarchical animations.
if (appStackClipMode == STACK_CLIP_BEFORE_ANIM && getRootTask() != null) {
@@ -4966,7 +4975,9 @@
*/
boolean updateTaskOrganizerState(boolean forceUpdate) {
if (!isRootTask()) {
- return false;
+ final boolean result = setTaskOrganizer(null);
+ mLastTaskOrganizerWindowingMode = -1;
+ return result;
}
final int windowingMode = getWindowingMode();
@@ -4985,7 +4996,7 @@
final ITaskOrganizer org =
mWmService.mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode);
final boolean result = setTaskOrganizer(org);
- mLastTaskOrganizerWindowingMode = windowingMode;
+ mLastTaskOrganizerWindowingMode = org != null ? windowingMode : -1;
return result;
}
@@ -5346,7 +5357,8 @@
}
if (isRootTask()) {
- taskDisplayArea.positionStackAtTop(this, false /* includingParents */, reason);
+ taskDisplayArea.positionChildAt(POSITION_TOP, this, false /* includingParents */,
+ reason);
}
if (task == null) {
task = this;
@@ -5374,7 +5386,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);
@@ -5400,12 +5413,9 @@
mCurrentUser = userId;
super.switchUser(userId);
- forAllLeafTasks((t) -> {
- if (t.showToCurrentUser() && t != this) {
- mChildren.remove(t);
- mChildren.add(t);
- }
- }, true /* traverseTopToBottom */);
+ if (isLeafTask() && showToCurrentUser()) {
+ getParent().positionChildAt(POSITION_TOP, this, false /*includeParents*/);
+ }
}
void minimalResumeActivityLocked(ActivityRecord r) {
@@ -7457,7 +7467,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}. */
@@ -7502,7 +7512,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,
@@ -7628,7 +7638,7 @@
private void updateSurfaceBounds() {
updateSurfaceSize(getSyncTransaction());
- updateSurfacePosition();
+ updateSurfacePositionNonOrganized();
scheduleAnimation();
}
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/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index d0785ff..f37fe3a 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -320,7 +320,7 @@
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
super.onConfigurationChanged(newParentConfig);
- updateSurfacePosition();
+ updateSurfacePositionNonOrganized();
scheduleAnimation();
}
@@ -418,7 +418,7 @@
setSurfaceControl(b.setCallsite("WindowContainer.setInitialSurfaceControlProperties").build());
getSyncTransaction().show(mSurfaceControl);
onSurfaceShown(getSyncTransaction());
- updateSurfacePosition();
+ updateSurfacePositionNonOrganized();
}
/**
@@ -2022,7 +2022,9 @@
}
protected void reparentSurfaceControl(Transaction t, SurfaceControl newParent) {
- mSurfaceAnimator.reparent(t, newParent);
+ // Don't reparent active leashes since the animator won't know about the change.
+ if (mSurfaceFreezer.hasLeash() || mSurfaceAnimator.hasLeash()) return;
+ t.reparent(getSurfaceControl(), newParent);
}
void assignChildLayers(Transaction t) {
@@ -2703,14 +2705,19 @@
}
}
- final void updateSurfacePosition() {
+ final void updateSurfacePositionNonOrganized() {
+ // Avoid fighting with the organizer over Surface position.
+ if (isOrganized()) return;
updateSurfacePosition(getSyncTransaction());
}
+ /**
+ * Only for use internally (see PROTECTED annotation). This should only be used over
+ * {@link #updateSurfacePositionNonOrganized} when the surface position needs to be
+ * updated even if organized (eg. while changing to organized).
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
void updateSurfacePosition(Transaction t) {
- // Avoid fighting with the organizer over Surface position.
- if (isOrganized()) return;
-
if (mSurfaceControl == null || mSurfaceAnimator.hasLeash()) {
return;
}
@@ -2750,13 +2757,6 @@
}
void getRelativePosition(Point outPos) {
- // In addition to updateSurfacePosition, we keep other code that sets
- // position from fighting with the organizer
- if (isOrganized()) {
- outPos.set(0, 0);
- return;
- }
-
final Rect dispBounds = getBounds();
outPos.set(dispBounds.left, dispBounds.top);
final WindowContainer parent = getParent();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7d15587..2dd25c9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1478,9 +1478,14 @@
rootType, attrs.token, attrs.packageName)) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
- final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
- token = new WindowToken(this, binder, type, false, displayContent,
- session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
+ if (hasParent) {
+ // Use existing parent window token for child windows.
+ token = parentWindow.mToken;
+ } else {
+ final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
+ token = new WindowToken(this, binder, type, false, displayContent,
+ session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
+ }
} else if (rootType >= FIRST_APPLICATION_WINDOW
&& rootType <= LAST_APPLICATION_WINDOW) {
activity = token.asActivityRecord();
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 8912d58..d25a648 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -241,13 +241,26 @@
final int configMask = change.getConfigSetMask() & CONTROLLABLE_CONFIGS;
final int windowMask = change.getWindowSetMask() & CONTROLLABLE_WINDOW_CONFIGS;
int effects = 0;
+ final int windowingMode = change.getWindowingMode();
if (configMask != 0) {
- Configuration c = new Configuration(container.getRequestedOverrideConfiguration());
- c.setTo(change.getConfiguration(), configMask, windowMask);
- container.onRequestedOverrideConfigurationChanged(c);
- // TODO(b/145675353): remove the following once we could apply new bounds to the
- // pinned stack together with its children.
- resizePinnedStackIfNeeded(container, configMask, windowMask, c);
+ if (windowingMode > -1 && windowingMode != container.getWindowingMode()) {
+ // Special handling for when we are setting a windowingMode in the same transaction.
+ // Setting the windowingMode is going to call onConfigurationChanged so we don't
+ // need it called right now. Additionally, some logic requires everything in the
+ // configuration to change at the same time (ie. surface-freezer requires bounds
+ // and mode to change at the same time).
+ final Configuration c = container.getRequestedOverrideConfiguration();
+ c.setTo(change.getConfiguration(), configMask, windowMask);
+ } else {
+ final Configuration c =
+ new Configuration(container.getRequestedOverrideConfiguration());
+ c.setTo(change.getConfiguration(), configMask, windowMask);
+ container.onRequestedOverrideConfigurationChanged(c);
+ // TODO(b/145675353): remove the following once we could apply new bounds to the
+ // pinned stack together with its children.
+ }
+ resizePinnedStackIfNeeded(container, configMask, windowMask,
+ container.getRequestedOverrideConfiguration());
effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
}
if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) {
@@ -256,7 +269,6 @@
}
}
- final int windowingMode = change.getWindowingMode();
if (windowingMode > -1) {
container.setWindowingMode(windowingMode);
}
@@ -345,29 +357,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.");
+ 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/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 9a48154..6ba8769 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -55,11 +55,14 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
+import android.os.Binder;
import android.os.Build;
+import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
@@ -98,6 +101,11 @@
final ApplicationInfo mInfo;
final String mName;
final int mUid;
+
+ // A set of tokens that currently contribute to this process being temporarily allowed
+ // to start activities even if it's not in the foreground. The values of this map are optional
+ // (can be null) and are used to trace back the grant to the notification token mechanism.
+ private final ArrayMap<Binder, IBinder> mBackgroundActivityStartTokens = new ArrayMap<>();
// The process of this application; 0 if none
private volatile int mPid;
// user of process.
@@ -160,9 +168,6 @@
private volatile boolean mPerceptible;
// Set to true when process was launched with a wrapper attached
private volatile boolean mUsingWrapper;
- // Set to true if this process is currently temporarily whitelisted to start activities even if
- // it's not in the foreground
- private volatile boolean mAllowBackgroundActivityStarts;
// Set of UIDs of clients currently bound to this process
private volatile ArraySet<Integer> mBoundClientUids = new ArraySet<Integer>();
@@ -208,6 +213,9 @@
/** Whether our process is currently running a {@link IRemoteAnimationRunner} */
private boolean mRunningRemoteAnimation;
+ @Nullable
+ private BackgroundActivityStartCallback mBackgroundActivityStartCallback;
+
public WindowProcessController(@NonNull ActivityTaskManagerService atm, ApplicationInfo info,
String name, int uid, int userId, Object owner, WindowProcessListener listener) {
mInfo = info;
@@ -218,6 +226,7 @@
mListener = listener;
mAtm = atm;
mDisplayId = INVALID_DISPLAY;
+ mBackgroundActivityStartCallback = mAtm.getBackgroundActivityStartCallback();
boolean isSysUiPackage = info.packageName.equals(
mAtm.getSysUiServiceComponentLocked().getPackageName());
@@ -449,19 +458,39 @@
mLastActivityFinishTime = finishTime;
}
- public void setAllowBackgroundActivityStarts(boolean allowBackgroundActivityStarts) {
- mAllowBackgroundActivityStarts = allowBackgroundActivityStarts;
+ /**
+ * Allows background activity starts using token {@code entity}. Optionally, you can provide
+ * {@code originatingToken} if you have one such originating token, this is useful for tracing
+ * back the grant in the case of the notification token.
+ */
+ public void addAllowBackgroundActivityStartsToken(Binder entity,
+ @Nullable IBinder originatingToken) {
+ synchronized (mAtm.mGlobalLock) {
+ mBackgroundActivityStartTokens.put(entity, originatingToken);
+ }
+ }
+
+ /**
+ * Removes token {@code entity} that allowed background activity starts added via {@link
+ * #addAllowBackgroundActivityStartsToken(Binder, IBinder)}.
+ */
+ public void removeAllowBackgroundActivityStartsToken(Binder entity) {
+ synchronized (mAtm.mGlobalLock) {
+ mBackgroundActivityStartTokens.remove(entity);
+ }
+ }
+
+ /**
+ * Returns true if background activity starts are allowed by any token added via {@link
+ * #addAllowBackgroundActivityStartsToken(Binder, IBinder)}.
+ */
+ public boolean areBackgroundActivityStartsAllowedByToken() {
+ synchronized (mAtm.mGlobalLock) {
+ return !mBackgroundActivityStartTokens.isEmpty();
+ }
}
boolean areBackgroundActivityStartsAllowed() {
- // allow if the whitelisting flag was explicitly set
- if (mAllowBackgroundActivityStarts) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "[WindowProcessController(" + mPid
- + ")] Activity start allowed: mAllowBackgroundActivityStarts = true");
- }
- return true;
- }
// allow if any activity in the caller has either started or finished very recently, and
// it must be started or finished after last stop app switches time.
final long now = SystemClock.uptimeMillis();
@@ -510,9 +539,32 @@
}
return true;
}
+ // allow if the flag was explicitly set
+ if (!mBackgroundActivityStartTokens.isEmpty()) {
+ onBackgroundStartAllowedByToken();
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(TAG, "[WindowProcessController(" + mPid
+ + ")] Activity start allowed: process allowed by token");
+ }
+ return true;
+ }
return false;
}
+ private void onBackgroundStartAllowedByToken() {
+ if (mBackgroundActivityStartCallback == null) {
+ return;
+ }
+ IBinder callbackToken = mBackgroundActivityStartCallback.getToken();
+ for (IBinder token : mBackgroundActivityStartTokens.values()) {
+ if (token != callbackToken) {
+ return;
+ }
+ }
+ mAtm.mH.post(() ->
+ mBackgroundActivityStartCallback.onExclusiveTokenActivityStart(mInfo.packageName));
+ }
+
private boolean isBoundByForegroundUid() {
for (int i = mBoundClientUids.size() - 1; i >= 0; --i) {
if (mAtm.isUidForeground(mBoundClientUids.valueAt(i))) {
@@ -1434,6 +1486,13 @@
if (mVrThreadTid != 0) {
pw.print(prefix); pw.print("mVrThreadTid="); pw.println(mVrThreadTid);
}
+ if (mBackgroundActivityStartTokens.size() > 0) {
+ pw.print(prefix); pw.println("Background activity start tokens:");
+ for (int i = 0; i < mBackgroundActivityStartTokens.size(); i++) {
+ pw.print(prefix); pw.print(" - ");
+ pw.println(mBackgroundActivityStartTokens.keyAt(i));
+ }
+ }
}
pw.println(prefix + " Configuration=" + getConfiguration());
pw.println(prefix + " OverrideConfiguration=" + getRequestedOverrideConfiguration());
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 1cbc950..ef78420 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.
@@ -2194,9 +2182,6 @@
if (dc.mInputMethodInputTarget == this) {
dc.setInputMethodInputTarget(null);
}
- if (dc.mInputMethodControlTarget == this) {
- dc.updateImeControlTarget();
- }
final int type = mAttrs.type;
if (WindowManagerService.excludeWindowTypeFromTapOutTask(type)) {
@@ -5397,7 +5382,7 @@
void prepareSurfaces() {
mIsDimming = false;
applyDims();
- updateSurfacePosition();
+ updateSurfacePositionNonOrganized();
// Send information to SufaceFlinger about the priority of the current window.
updateFrameRateSelectionPriorityIfNeeded();
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 7bd455a..10523a2 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -200,10 +200,10 @@
void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
- status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
- status_t registerInputMonitor(JNIEnv* env, const sp<InputChannel>& inputChannel,
- int32_t displayId, bool isGestureMonitor);
- status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
+ status_t registerInputChannel(JNIEnv* env, const std::shared_ptr<InputChannel>& inputChannel);
+ status_t registerInputMonitor(JNIEnv* env, const std::shared_ptr<InputChannel>& inputChannel,
+ int32_t displayId, bool isGestureMonitor);
+ status_t unregisterInputChannel(JNIEnv* env, const InputChannel& inputChannel);
status_t pilferPointers(const sp<IBinder>& token);
void displayRemoved(JNIEnv* env, int32_t displayId);
@@ -421,21 +421,22 @@
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
}
-status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
- const sp<InputChannel>& inputChannel) {
+status_t NativeInputManager::registerInputChannel(
+ JNIEnv* /* env */, const std::shared_ptr<InputChannel>& inputChannel) {
ATRACE_CALL();
return mInputManager->getDispatcher()->registerInputChannel(inputChannel);
}
status_t NativeInputManager::registerInputMonitor(JNIEnv* /* env */,
- const sp<InputChannel>& inputChannel, int32_t displayId, bool isGestureMonitor) {
+ const std::shared_ptr<InputChannel>& inputChannel,
+ int32_t displayId, bool isGestureMonitor) {
ATRACE_CALL();
return mInputManager->getDispatcher()->registerInputMonitor(
inputChannel, displayId, isGestureMonitor);
}
status_t NativeInputManager::unregisterInputChannel(JNIEnv* /* env */,
- const sp<InputChannel>& inputChannel) {
+ const InputChannel& inputChannel) {
ATRACE_CALL();
return mInputManager->getDispatcher()->unregisterInputChannel(inputChannel);
}
@@ -1338,21 +1339,22 @@
"inputChannel is not initialized");
}
-static void handleInputChannelDisposed(JNIEnv* env,
- jobject /* inputChannelObj */, const sp<InputChannel>& inputChannel, void* data) {
+static void handleInputChannelDisposed(JNIEnv* env, jobject /* inputChannelObj */,
+ const std::shared_ptr<InputChannel>& inputChannel,
+ void* data) {
NativeInputManager* im = static_cast<NativeInputManager*>(data);
ALOGW("Input channel object '%s' was disposed without first being unregistered with "
"the input manager!", inputChannel->getName().c_str());
- im->unregisterInputChannel(env, inputChannel);
+ im->unregisterInputChannel(env, *inputChannel);
}
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
jlong ptr, jobject inputChannelObj) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
+ std::shared_ptr<InputChannel> inputChannel =
+ android_view_InputChannel_getInputChannel(env, inputChannelObj);
if (inputChannel == nullptr) {
throwInputChannelNotInitialized(env);
return;
@@ -1375,8 +1377,8 @@
jlong ptr, jobject inputChannelObj, jint displayId, jboolean isGestureMonitor) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
+ std::shared_ptr<InputChannel> inputChannel =
+ android_view_InputChannel_getInputChannel(env, inputChannelObj);
if (inputChannel == nullptr) {
throwInputChannelNotInitialized(env);
return;
@@ -1401,8 +1403,8 @@
jlong ptr, jobject inputChannelObj) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
+ std::shared_ptr<InputChannel> inputChannel =
+ android_view_InputChannel_getInputChannel(env, inputChannelObj);
if (inputChannel == nullptr) {
throwInputChannelNotInitialized(env);
return;
@@ -1410,7 +1412,7 @@
android_view_InputChannel_setDisposeCallback(env, inputChannelObj, nullptr, nullptr);
- status_t status = im->unregisterInputChannel(env, inputChannel);
+ status_t status = im->unregisterInputChannel(env, *inputChannel);
if (status && status != BAD_VALUE) { // ignore already unregistered channel
std::string message;
message += StringPrintf("Failed to unregister input channel. status=%d", status);
@@ -1615,9 +1617,8 @@
im->reloadCalibration();
}
-static void nativeVibrate(JNIEnv* env,
- jclass /* clazz */, jlong ptr, jint deviceId, jlongArray patternObj,
- jint repeat, jint token) {
+static void nativeVibrate(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId,
+ jlongArray patternObj, jintArray amplitudesObj, jint repeat, jint token) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
size_t patternSize = env->GetArrayLength(patternObj);
@@ -1630,14 +1631,19 @@
jlong* patternMillis = static_cast<jlong*>(env->GetPrimitiveArrayCritical(
patternObj, nullptr));
- nsecs_t pattern[patternSize];
+ jint* amplitudes = static_cast<jint*>(env->GetPrimitiveArrayCritical(amplitudesObj, nullptr));
+
+ std::vector<VibrationElement> pattern(patternSize);
for (size_t i = 0; i < patternSize; i++) {
- pattern[i] = max(jlong(0), min(patternMillis[i],
- (jlong)(MAX_VIBRATE_PATTERN_DELAY_NSECS / 1000000LL))) * 1000000LL;
+ jlong duration =
+ max(min(patternMillis[i], (jlong)MAX_VIBRATE_PATTERN_DELAY_MSECS), (jlong)0);
+ pattern[i].duration = std::chrono::milliseconds(duration);
+ pattern[i].channels = {amplitudes[i]};
}
env->ReleasePrimitiveArrayCritical(patternObj, patternMillis, JNI_ABORT);
+ env->ReleasePrimitiveArrayCritical(amplitudesObj, amplitudes, JNI_ABORT);
- im->getInputManager()->getReader()->vibrate(deviceId, pattern, patternSize, repeat, token);
+ im->getInputManager()->getReader()->vibrate(deviceId, pattern, repeat, token);
}
static void nativeCancelVibrate(JNIEnv* /* env */,
@@ -1789,7 +1795,7 @@
{"nativeSetShowTouches", "(JZ)V", (void*)nativeSetShowTouches},
{"nativeSetInteractive", "(JZ)V", (void*)nativeSetInteractive},
{"nativeReloadCalibration", "(J)V", (void*)nativeReloadCalibration},
- {"nativeVibrate", "(JI[JII)V", (void*)nativeVibrate},
+ {"nativeVibrate", "(JI[J[III)V", (void*)nativeVibrate},
{"nativeCancelVibrate", "(JII)V", (void*)nativeCancelVibrate},
{"nativeReloadKeyboardLayouts", "(J)V", (void*)nativeReloadKeyboardLayouts},
{"nativeReloadDeviceAliases", "(J)V", (void*)nativeReloadDeviceAliases},
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 6140531..91f7072 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -29,7 +29,6 @@
#include <nativehelper/ScopedUtfChars.h>
#include <powermanager/PowerHalController.h>
-#include <powermanager/PowerHalLoader.h>
#include <limits.h>
@@ -49,12 +48,8 @@
#include "com_android_server_power_PowerManagerService.h"
using android::String8;
-using android::hardware::Return;
-using android::hardware::Void;
using android::hardware::power::Boost;
using android::hardware::power::Mode;
-using android::hardware::power::V1_0::Feature;
-using android::hardware::power::V1_0::PowerHint;
using android::system::suspend::ISuspendControlService;
using android::system::suspend::V1_0::ISystemSuspend;
using android::system::suspend::V1_0::IWakeLock;
@@ -74,18 +69,7 @@
// ----------------------------------------------------------------------------
static jobject gPowerManagerServiceObj;
-static sp<IPowerV1_0> gPowerHalHidlV1_0_ = nullptr;
-static sp<IPowerV1_1> gPowerHalHidlV1_1_ = nullptr;
-static sp<IPowerAidl> gPowerHalAidl_ = nullptr;
-static std::mutex gPowerHalMutex;
-
-enum class HalVersion {
- NONE,
- HIDL_1_0,
- HIDL_1_1,
- AIDL,
-};
-
+static power::PowerHalController gPowerHalController;
static nsecs_t gLastEventTime[USER_ACTIVITY_EVENT_LAST + 1];
// Throttling interval for user activity calls.
@@ -103,208 +87,19 @@
return false;
}
-// Check validity of current handle to the power HAL service, and connect to it if necessary.
-// The caller must be holding gPowerHalMutex.
-static HalVersion connectPowerHalLocked() {
- static bool gPowerHalHidlExists = true;
- static bool gPowerHalAidlExists = true;
- if (!gPowerHalHidlExists && !gPowerHalAidlExists) {
- return HalVersion::NONE;
- }
- if (gPowerHalAidlExists) {
- if (!gPowerHalAidl_) {
- gPowerHalAidl_ = waitForVintfService<IPowerAidl>();
- }
- if (gPowerHalAidl_) {
- ALOGV("Successfully connected to Power HAL AIDL service.");
- return HalVersion::AIDL;
- } else {
- gPowerHalAidlExists = false;
- }
- }
- if (gPowerHalHidlExists && gPowerHalHidlV1_0_ == nullptr) {
- gPowerHalHidlV1_0_ = IPowerV1_0::getService();
- if (gPowerHalHidlV1_0_) {
- ALOGV("Successfully connected to Power HAL HIDL 1.0 service.");
- // Try cast to powerHAL HIDL V1_1
- gPowerHalHidlV1_1_ = IPowerV1_1::castFrom(gPowerHalHidlV1_0_);
- if (gPowerHalHidlV1_1_) {
- ALOGV("Successfully connected to Power HAL HIDL 1.1 service.");
- }
- } else {
- ALOGV("Couldn't load power HAL HIDL service");
- gPowerHalHidlExists = false;
- return HalVersion::NONE;
- }
- }
- if (gPowerHalHidlV1_1_) {
- return HalVersion::HIDL_1_1;
- } else if (gPowerHalHidlV1_0_) {
- return HalVersion::HIDL_1_0;
- }
- return HalVersion::NONE;
-}
-
-// Check if a call to a power HAL function failed; if so, log the failure and invalidate the
-// current handle to the power HAL service.
-bool processPowerHalReturn(bool isOk, const char* functionName) {
- if (!isOk) {
- ALOGE("%s() failed: power HAL service not available.", functionName);
- gPowerHalMutex.lock();
- gPowerHalHidlV1_0_ = nullptr;
- gPowerHalHidlV1_1_ = nullptr;
- gPowerHalAidl_ = nullptr;
- gPowerHalMutex.unlock();
- }
- return isOk;
-}
-
-enum class HalSupport {
- UNKNOWN = 0,
- ON,
- OFF,
-};
-
-static void setPowerBoostWithHandle(sp<IPowerAidl> handle, Boost boost, int32_t durationMs) {
- // Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT.
- // Need to increase the array size if more boost supported.
- static std::array<std::atomic<HalSupport>,
- static_cast<int32_t>(Boost::DISPLAY_UPDATE_IMMINENT) + 1>
- boostSupportedArray = {HalSupport::UNKNOWN};
-
- // Quick return if boost is not supported by HAL
- if (boost > Boost::DISPLAY_UPDATE_IMMINENT ||
- boostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::OFF) {
- ALOGV("Skipped setPowerBoost %s because HAL doesn't support it", toString(boost).c_str());
- return;
- }
-
- if (boostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::UNKNOWN) {
- bool isSupported = false;
- handle->isBoostSupported(boost, &isSupported);
- boostSupportedArray[static_cast<int32_t>(boost)] =
- isSupported ? HalSupport::ON : HalSupport::OFF;
- if (!isSupported) {
- ALOGV("Skipped setPowerBoost %s because HAL doesn't support it",
- toString(boost).c_str());
- return;
- }
- }
-
- auto ret = handle->setBoost(boost, durationMs);
- processPowerHalReturn(ret.isOk(), "setPowerBoost");
-}
-
static void setPowerBoost(Boost boost, int32_t durationMs) {
- std::unique_lock<std::mutex> lock(gPowerHalMutex);
- if (connectPowerHalLocked() != HalVersion::AIDL) {
- ALOGV("Power HAL AIDL not available");
- return;
- }
- sp<IPowerAidl> handle = gPowerHalAidl_;
- lock.unlock();
- setPowerBoostWithHandle(handle, boost, durationMs);
-}
-
-static bool setPowerModeWithHandle(sp<IPowerAidl> handle, Mode mode, bool enabled) {
- // Android framework only sends mode upto DISPLAY_INACTIVE.
- // Need to increase the array if more mode supported.
- static std::array<std::atomic<HalSupport>, static_cast<int32_t>(Mode::DISPLAY_INACTIVE) + 1>
- modeSupportedArray = {HalSupport::UNKNOWN};
-
- // Quick return if mode is not supported by HAL
- if (mode > Mode::DISPLAY_INACTIVE ||
- modeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::OFF) {
- ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str());
- return false;
- }
-
- if (modeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::UNKNOWN) {
- bool isSupported = false;
- handle->isModeSupported(mode, &isSupported);
- modeSupportedArray[static_cast<int32_t>(mode)] =
- isSupported ? HalSupport::ON : HalSupport::OFF;
- if (!isSupported) {
- ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str());
- return false;
- }
- }
-
- auto ret = handle->setMode(mode, enabled);
- processPowerHalReturn(ret.isOk(), "setPowerMode");
- return ret.isOk();
+ gPowerHalController.setBoost(boost, durationMs);
+ SurfaceComposerClient::notifyPowerBoost(static_cast<int32_t>(boost));
}
static bool setPowerMode(Mode mode, bool enabled) {
- std::unique_lock<std::mutex> lock(gPowerHalMutex);
- if (connectPowerHalLocked() != HalVersion::AIDL) {
- ALOGV("Power HAL AIDL not available");
- return false;
+ android::base::Timer t;
+ auto result = gPowerHalController.setMode(mode, enabled);
+ if (mode == Mode::INTERACTIVE && t.duration() > 20ms) {
+ ALOGD("Excessive delay in setting interactive mode to %s while turning screen %s",
+ enabled ? "true" : "false", enabled ? "on" : "off");
}
- sp<IPowerAidl> handle = gPowerHalAidl_;
- lock.unlock();
- return setPowerModeWithHandle(handle, mode, enabled);
-}
-
-static void sendPowerHint(PowerHint hintId, uint32_t data) {
- std::unique_lock<std::mutex> lock(gPowerHalMutex);
- switch (connectPowerHalLocked()) {
- case HalVersion::NONE:
- return;
- case HalVersion::HIDL_1_0: {
- sp<IPowerV1_0> handle = gPowerHalHidlV1_0_;
- lock.unlock();
- auto ret = handle->powerHint(hintId, data);
- processPowerHalReturn(ret.isOk(), "powerHint");
- break;
- }
- case HalVersion::HIDL_1_1: {
- sp<IPowerV1_1> handle = gPowerHalHidlV1_1_;
- lock.unlock();
- auto ret = handle->powerHintAsync(hintId, data);
- processPowerHalReturn(ret.isOk(), "powerHintAsync");
- break;
- }
- case HalVersion::AIDL: {
- if (hintId == PowerHint::INTERACTION) {
- sp<IPowerAidl> handle = gPowerHalAidl_;
- lock.unlock();
- setPowerBoostWithHandle(handle, Boost::INTERACTION, data);
- break;
- } else if (hintId == PowerHint::LAUNCH) {
- sp<IPowerAidl> handle = gPowerHalAidl_;
- lock.unlock();
- setPowerModeWithHandle(handle, Mode::LAUNCH, static_cast<bool>(data));
- break;
- } else if (hintId == PowerHint::LOW_POWER) {
- sp<IPowerAidl> handle = gPowerHalAidl_;
- lock.unlock();
- setPowerModeWithHandle(handle, Mode::LOW_POWER, static_cast<bool>(data));
- break;
- } else if (hintId == PowerHint::SUSTAINED_PERFORMANCE) {
- sp<IPowerAidl> handle = gPowerHalAidl_;
- lock.unlock();
- setPowerModeWithHandle(handle, Mode::SUSTAINED_PERFORMANCE,
- static_cast<bool>(data));
- break;
- } else if (hintId == PowerHint::VR_MODE) {
- sp<IPowerAidl> handle = gPowerHalAidl_;
- lock.unlock();
- setPowerModeWithHandle(handle, Mode::VR, static_cast<bool>(data));
- break;
- } else {
- ALOGE("Unsupported power hint: %s.", toString(hintId).c_str());
- return;
- }
- }
- default: {
- ALOGE("Unknown power HAL state");
- return;
- }
- }
- if (hintId == PowerHint::INTERACTION) {
- SurfaceComposerClient::notifyPowerBoost(static_cast<int32_t>(Boost::INTERACTION));
- }
+ return result == power::HalResult::SUCCESSFUL;
}
void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) {
@@ -324,7 +119,7 @@
gLastEventTime[eventType] = eventTime;
// Tell the power HAL when user activity occurs.
- sendPowerHint(PowerHint::INTERACTION, 0);
+ setPowerBoost(Boost::INTERACTION, 0);
}
JNIEnv* env = AndroidRuntime::getJNIEnv();
@@ -391,10 +186,7 @@
static void nativeInit(JNIEnv* env, jobject obj) {
gPowerManagerServiceObj = env->NewGlobalRef(obj);
-
- gPowerHalMutex.lock();
- connectPowerHalLocked();
- gPowerHalMutex.unlock();
+ gPowerHalController.init();
}
static void nativeAcquireSuspendBlocker(JNIEnv *env, jclass /* clazz */, jstring nameStr) {
@@ -407,38 +199,6 @@
release_wake_lock(name.c_str());
}
-static void nativeSetInteractive(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) {
- std::unique_lock<std::mutex> lock(gPowerHalMutex);
- switch (connectPowerHalLocked()) {
- case HalVersion::NONE:
- return;
- case HalVersion::HIDL_1_0:
- FALLTHROUGH_INTENDED;
- case HalVersion::HIDL_1_1: {
- android::base::Timer t;
- sp<IPowerV1_0> handle = gPowerHalHidlV1_0_;
- lock.unlock();
- auto ret = handle->setInteractive(enable);
- processPowerHalReturn(ret.isOk(), "setInteractive");
- if (t.duration() > 20ms) {
- ALOGD("Excessive delay in setInteractive(%s) while turning screen %s",
- enable ? "true" : "false", enable ? "on" : "off");
- }
- return;
- }
- case HalVersion::AIDL: {
- sp<IPowerAidl> handle = gPowerHalAidl_;
- lock.unlock();
- setPowerModeWithHandle(handle, Mode::INTERACTIVE, enable);
- return;
- }
- default: {
- ALOGE("Unknown power HAL state");
- return;
- }
- }
-}
-
static void nativeSetAutoSuspend(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) {
if (enable) {
android::base::Timer t;
@@ -455,10 +215,6 @@
}
}
-static void nativeSendPowerHint(JNIEnv* /* env */, jclass /* clazz */, jint hintId, jint data) {
- sendPowerHint(static_cast<PowerHint>(hintId), data);
-}
-
static void nativeSetPowerBoost(JNIEnv* /* env */, jclass /* clazz */, jint boost,
jint durationMs) {
setPowerBoost(static_cast<Boost>(boost), durationMs);
@@ -469,33 +225,6 @@
return setPowerMode(static_cast<Mode>(mode), enabled);
}
-static void nativeSetFeature(JNIEnv* /* env */, jclass /* clazz */, jint featureId, jint data) {
- std::unique_lock<std::mutex> lock(gPowerHalMutex);
- switch (connectPowerHalLocked()) {
- case HalVersion::NONE:
- return;
- case HalVersion::HIDL_1_0:
- FALLTHROUGH_INTENDED;
- case HalVersion::HIDL_1_1: {
- sp<IPowerV1_0> handle = gPowerHalHidlV1_0_;
- lock.unlock();
- auto ret = handle->setFeature(static_cast<Feature>(featureId), static_cast<bool>(data));
- processPowerHalReturn(ret.isOk(), "setFeature");
- return;
- }
- case HalVersion::AIDL: {
- sp<IPowerAidl> handle = gPowerHalAidl_;
- lock.unlock();
- setPowerModeWithHandle(handle, Mode::DOUBLE_TAP_TO_WAKE, static_cast<bool>(data));
- return;
- }
- default: {
- ALOGE("Unknown power HAL state");
- return;
- }
- }
-}
-
static bool nativeForceSuspend(JNIEnv* /* env */, jclass /* clazz */) {
bool retval = false;
getSuspendControl()->forceSuspend(&retval);
@@ -512,12 +241,9 @@
{"nativeForceSuspend", "()Z", (void*)nativeForceSuspend},
{"nativeReleaseSuspendBlocker", "(Ljava/lang/String;)V",
(void*)nativeReleaseSuspendBlocker},
- {"nativeSetInteractive", "(Z)V", (void*)nativeSetInteractive},
{"nativeSetAutoSuspend", "(Z)V", (void*)nativeSetAutoSuspend},
- {"nativeSendPowerHint", "(II)V", (void*)nativeSendPowerHint},
{"nativeSetPowerBoost", "(II)V", (void*)nativeSetPowerBoost},
{"nativeSetPowerMode", "(IZ)Z", (void*)nativeSetPowerMode},
- {"nativeSetFeature", "(II)V", (void*)nativeSetFeature},
};
#define FIND_CLASS(var, className) \
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/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a2e310a..ae6ccda 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1119,7 +1119,7 @@
t.traceEnd();
t.traceBegin("InstallSystemProviders");
- mActivityManagerService.installSystemProviders();
+ mActivityManagerService.getContentProviderHelper().installSystemProviders();
// Now that SettingsProvider is ready, reactivate SQLiteCompatibilityWalFlags
SQLiteCompatibilityWalFlags.reset();
t.traceEnd();
diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp
index 25ab5d3..1ae2aec 100644
--- a/services/robotests/Android.bp
+++ b/services/robotests/Android.bp
@@ -43,6 +43,7 @@
// Include the testing libraries
libs: [
"platform-test-annotations",
+ "services.backup",
"testng",
],
static_libs: [
diff --git a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
index dcd7af3..8ccaedd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
@@ -18,7 +18,7 @@
import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
-import static com.android.server.AppStateTracker.TARGET_OP;
+import static com.android.server.AppStateTrackerImpl.TARGET_OP;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -68,7 +68,7 @@
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
-import com.android.server.AppStateTracker.Listener;
+import com.android.server.AppStateTrackerImpl.Listener;
import com.android.server.usage.AppStandbyInternal;
import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
@@ -91,7 +91,7 @@
import java.util.function.Consumer;
/**
- * Tests for {@link AppStateTracker}
+ * Tests for {@link AppStateTrackerImpl}
*
* Run with: atest com.android.server.AppStateTrackerTest
*/
@@ -100,7 +100,7 @@
@RunWith(AndroidJUnit4.class)
public class AppStateTrackerTest {
- private class AppStateTrackerTestable extends AppStateTracker {
+ private class AppStateTrackerTestable extends AppStateTrackerImpl {
AppStateTrackerTestable() {
super(mMockContext, Looper.getMainLooper());
}
@@ -722,7 +722,7 @@
AppOpsManager.MODE_IGNORED,
Collections.emptyMap()));
entries.add(new OpEntry(
- AppStateTracker.TARGET_OP,
+ AppStateTrackerImpl.TARGET_OP,
AppOpsManager.MODE_IGNORED,
Collections.emptyMap()));
@@ -731,7 +731,7 @@
//--------------------------------------------------
entries = new ArrayList<>();
entries.add(new OpEntry(
- AppStateTracker.TARGET_OP,
+ AppStateTrackerImpl.TARGET_OP,
AppOpsManager.MODE_IGNORED,
Collections.emptyMap()));
@@ -740,7 +740,7 @@
//--------------------------------------------------
entries = new ArrayList<>();
entries.add(new OpEntry(
- AppStateTracker.TARGET_OP,
+ AppStateTrackerImpl.TARGET_OP,
AppOpsManager.MODE_ALLOWED,
Collections.emptyMap()));
@@ -749,7 +749,7 @@
//--------------------------------------------------
entries = new ArrayList<>();
entries.add(new OpEntry(
- AppStateTracker.TARGET_OP,
+ AppStateTrackerImpl.TARGET_OP,
AppOpsManager.MODE_IGNORED,
Collections.emptyMap()));
entries.add(new OpEntry(
@@ -1266,7 +1266,7 @@
private void checkAnyAppIdUnwhitelisted(int[] prevArray, int[] newArray, boolean expected) {
assertEquals("Input: " + Arrays.toString(prevArray) + " " + Arrays.toString(newArray),
- expected, AppStateTracker.isAnyAppIdUnwhitelisted(prevArray, newArray));
+ expected, AppStateTrackerImpl.isAnyAppIdUnwhitelisted(prevArray, newArray));
// Also test isAnyAppIdUnwhitelistedSlow.
assertEquals("Input: " + Arrays.toString(prevArray) + " " + Arrays.toString(newArray),
@@ -1298,7 +1298,7 @@
final int[] array2 = makeRandomArray();
final boolean expected = isAnyAppIdUnwhitelistedSlow(array1, array2);
- final boolean actual = AppStateTracker.isAnyAppIdUnwhitelisted(array1, array2);
+ final boolean actual = AppStateTrackerImpl.isAnyAppIdUnwhitelisted(array1, array2);
assertEquals("Input: " + Arrays.toString(array1) + " " + Arrays.toString(array2),
expected, actual);
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 9251031..6cd083e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -164,7 +164,7 @@
}
@Override
- AppStateTracker getAppStateTracker(Context ctx, Looper loop) {
+ AppStateTrackerImpl getAppStateTracker(Context ctx, Looper loop) {
return mAppStateTracker;
}
@@ -260,7 +260,7 @@
}
}
- private class AppStateTrackerForTest extends AppStateTracker {
+ private class AppStateTrackerForTest extends AppStateTrackerImpl {
AppStateTrackerForTest(Context ctx, Looper looper) {
super(ctx, looper);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 6c9b6182..77232dc 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -89,6 +89,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.server.AlarmManagerInternal;
import com.android.server.AppStateTracker;
+import com.android.server.AppStateTrackerImpl;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.usage.AppStandbyInternal;
@@ -131,7 +132,7 @@
@Mock
private ActivityManagerInternal mActivityManagerInternal;
@Mock
- private AppStateTracker mAppStateTracker;
+ private AppStateTrackerImpl mAppStateTracker;
@Mock
private AlarmManagerService.ClockReceiver mClockReceiver;
@Mock
@@ -769,8 +770,8 @@
@Test
public void testAlarmRestrictedInBatterSaver() throws Exception {
- final ArgumentCaptor<AppStateTracker.Listener> listenerArgumentCaptor =
- ArgumentCaptor.forClass(AppStateTracker.Listener.class);
+ final ArgumentCaptor<AppStateTrackerImpl.Listener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(AppStateTrackerImpl.Listener.class);
verify(mAppStateTracker).addListener(listenerArgumentCaptor.capture());
final PendingIntent alarmPi = getNewMockPendingIntent();
@@ -795,10 +796,10 @@
@Test
public void alarmsRemovedOnAppStartModeDisabled() {
- final ArgumentCaptor<AppStateTracker.Listener> listenerArgumentCaptor =
- ArgumentCaptor.forClass(AppStateTracker.Listener.class);
+ final ArgumentCaptor<AppStateTrackerImpl.Listener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(AppStateTrackerImpl.Listener.class);
verify(mAppStateTracker).addListener(listenerArgumentCaptor.capture());
- final AppStateTracker.Listener listener = listenerArgumentCaptor.getValue();
+ final AppStateTrackerImpl.Listener listener = listenerArgumentCaptor.getValue();
final PendingIntent alarmPi1 = getNewMockPendingIntent();
final PendingIntent alarmPi2 = getNewMockPendingIntent();
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index a462dc3..043ca9e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -55,6 +55,7 @@
import android.os.SystemClock;
import com.android.server.AppStateTracker;
+import com.android.server.AppStateTrackerImpl;
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
import com.android.server.SystemServiceManager;
@@ -84,7 +85,7 @@
private class TestJobSchedulerService extends JobSchedulerService {
TestJobSchedulerService(Context context) {
super(context);
- mAppStateTracker = mock(AppStateTracker.class);
+ mAppStateTracker = mock(AppStateTrackerImpl.class);
}
@Override
@@ -112,7 +113,7 @@
.when(() -> LocalServices.getService(UsageStatsManagerInternal.class));
when(mContext.getString(anyInt())).thenReturn("some_test_string");
// Called in BackgroundJobsController constructor.
- doReturn(mock(AppStateTracker.class))
+ doReturn(mock(AppStateTrackerImpl.class))
.when(() -> LocalServices.getService(AppStateTracker.class));
// Called in BatteryController constructor.
doReturn(mock(BatteryManagerInternal.class))
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
index 5d041b7..3614763 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
@@ -71,7 +71,6 @@
private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
private static final int SOURCE_USER_ID = 0;
- private TimeController.TcConstants mConstants;
private TimeController mTimeController;
private MockitoSession mMockingSession;
@@ -111,7 +110,6 @@
// Initialize real objects.
mTimeController = new TimeController(mJobSchedulerService);
- mConstants = mTimeController.getTcConstants();
spyOn(mTimeController);
}
@@ -530,46 +528,6 @@
}
@Test
- public void testJobDelayWakeupAlarmToggling() {
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
-
- JobStatus job = createJobStatus(
- "testMaybeStartTrackingJobLocked_DeadlineReverseOrder",
- createJob().setMinimumLatency(HOUR_IN_MILLIS));
-
- doReturn(true).when(mTimeController)
- .wouldBeReadyWithConstraintLocked(eq(job), anyInt());
-
- // Starting off with using a wakeup alarm.
- mConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY = false;
- InOrder inOrder = inOrder(mAlarmManager);
-
- mTimeController.maybeStartTrackingJobLocked(job, null);
- inOrder.verify(mAlarmManager, times(1))
- .set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq(now + HOUR_IN_MILLIS), anyLong(),
- anyLong(),
- eq(TAG_DELAY), any(), any(), any());
-
- // Use a non wakeup alarm.
- mConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY = true;
-
- mTimeController.maybeStartTrackingJobLocked(job, null);
- inOrder.verify(mAlarmManager, times(1))
- .set(eq(AlarmManager.ELAPSED_REALTIME), eq(now + HOUR_IN_MILLIS), anyLong(),
- anyLong(), eq(TAG_DELAY),
- any(), any(), any());
-
- // Back off, use a wakeup alarm.
- mConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY = false;
-
- mTimeController.maybeStartTrackingJobLocked(job, null);
- inOrder.verify(mAlarmManager, times(1))
- .set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq(now + HOUR_IN_MILLIS), anyLong(),
- anyLong(),
- eq(TAG_DELAY), any(), any(), any());
- }
-
- @Test
public void testCheckExpiredDeadlinesAndResetAlarm_AllReady() {
doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());
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/am/BroadcastRecordTest.java b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
index a871ec6..5bef877 100644
--- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -195,7 +195,8 @@
false /* sticky */,
false /* initialSticky */,
userId,
- false, /* allowBackgroundActivityStarts */
+ false /* allowBackgroundActivityStarts */,
+ null /* activityStartsToken */,
false /* timeoutExempt */ );
}
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/DataChangedJournalTest.java b/services/tests/servicestests/src/com/android/server/backup/DataChangedJournalTest.java
index 0e918db..9daa4ba 100644
--- a/services/tests/servicestests/src/com/android/server/backup/DataChangedJournalTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/DataChangedJournalTest.java
@@ -39,7 +39,7 @@
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
-import java.util.ArrayList;
+import java.io.IOException;
import java.util.function.Consumer;
@SmallTest
@@ -90,7 +90,13 @@
@Test
public void equals_isTrueForTheSameFile() throws Exception {
- assertThat(mJournal.equals(new DataChangedJournal(mFile))).isTrue();
+ assertEqualsBothWaysAndHashCode(mJournal, new DataChangedJournal(mFile));
+ }
+
+ private static <T> void assertEqualsBothWaysAndHashCode(T a, T b) {
+ assertEquals(a, b);
+ assertEquals(b, a);
+ assertEquals(a.hashCode(), b.hashCode());
}
@Test
@@ -117,9 +123,7 @@
DataChangedJournal.newJournal(folder);
DataChangedJournal.newJournal(folder);
- ArrayList<DataChangedJournal> journals = DataChangedJournal.listJournals(folder);
-
- assertThat(journals).hasSize(2);
+ assertThat(DataChangedJournal.listJournals(folder)).hasSize(2);
}
@Test
@@ -131,6 +135,16 @@
assertThat(folder.listFiles()).hasLength(1);
}
+ @Test(expected = NullPointerException.class)
+ public void newJournal_nullJournalDir() throws IOException {
+ DataChangedJournal.newJournal(null);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void nullFile() {
+ new DataChangedJournal(null);
+ }
+
@Test
public void toString_isSameAsFileToString() throws Exception {
assertThat(mJournal.toString()).isEqualTo(mFile.toString());
@@ -140,6 +154,6 @@
public void listJournals_invalidJournalFile_returnsEmptyList() throws Exception {
when(invalidFile.listFiles()).thenReturn(null);
- assertEquals(0, DataChangedJournal.listJournals(invalidFile).size());
+ assertThat(DataChangedJournal.listJournals(invalidFile)).isEmpty();
}
}
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/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 9669010..419fb14 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -369,9 +369,11 @@
}
@Test
- public void testCreateService_initializesNativeService() {
+ public void testCreateService_initializesNativeServiceAndSetsPowerModes() {
PowerManagerService service = createService();
verify(mNativeWrapperMock).nativeInit(same(service));
+ verify(mNativeWrapperMock).nativeSetPowerMode(eq(Mode.INTERACTIVE), eq(true));
+ verify(mNativeWrapperMock).nativeSetPowerMode(eq(Mode.DOUBLE_TAP_TO_WAKE), eq(false));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
index 76239fc..72d6caf 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
@@ -33,6 +33,7 @@
import com.android.server.power.batterysaver.BatterySavingStats.BatterySaverState;
import com.android.server.power.batterysaver.BatterySavingStats.DozeState;
import com.android.server.power.batterysaver.BatterySavingStats.InteractiveState;
+import com.android.server.power.batterysaver.BatterySavingStats.PlugState;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -118,7 +119,8 @@
target.transitionState(
BatterySaverState.OFF,
InteractiveState.INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
target.advanceClock(4);
target.drainBattery(100);
@@ -126,7 +128,8 @@
target.transitionState(
BatterySaverState.OFF,
InteractiveState.NON_INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
target.advanceClock(2);
target.drainBattery(500);
@@ -134,7 +137,8 @@
target.transitionState(
BatterySaverState.OFF,
InteractiveState.INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
target.advanceClock(4);
target.drainBattery(100);
@@ -142,7 +146,8 @@
target.transitionState(
BatterySaverState.OFF,
InteractiveState.NON_INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
target.advanceClock(2);
target.drainBattery(500);
@@ -150,7 +155,8 @@
target.transitionState(
BatterySaverState.OFF,
InteractiveState.INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
target.advanceClock(3);
target.drainBattery(100);
@@ -158,7 +164,8 @@
target.transitionState(
BatterySaverState.OFF,
InteractiveState.NON_INTERACTIVE,
- DozeState.LIGHT);
+ DozeState.LIGHT,
+ PlugState.UNPLUGGED);
target.advanceClock(5);
target.drainBattery(100);
@@ -166,7 +173,8 @@
target.transitionState(
BatterySaverState.OFF,
InteractiveState.NON_INTERACTIVE,
- DozeState.DEEP);
+ DozeState.DEEP,
+ PlugState.UNPLUGGED);
target.advanceClock(1);
target.drainBattery(200);
@@ -174,7 +182,8 @@
target.transitionState(
BatterySaverState.ON,
InteractiveState.INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
target.advanceClock(1);
target.drainBattery(300);
@@ -182,7 +191,8 @@
target.transitionState(
BatterySaverState.OFF,
InteractiveState.INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
target.advanceClock(3);
target.drainBattery(500);
@@ -190,12 +200,17 @@
target.transitionState(
BatterySaverState.ON,
InteractiveState.INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
target.advanceClock(3);
target.drainBattery(500);
- target.startCharging();
+ target.transitionState(
+ BatterySaverState.ON,
+ InteractiveState.INTERACTIVE,
+ DozeState.NOT_DOZING,
+ PlugState.PLUGGED);
target.advanceClock(5);
target.drainBattery(1000);
@@ -203,28 +218,34 @@
target.transitionState(
BatterySaverState.ON,
InteractiveState.INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
target.advanceClock(5);
target.drainBattery(100);
- target.startCharging();
+ target.transitionState(
+ BatterySaverState.ON,
+ InteractiveState.INTERACTIVE,
+ DozeState.NOT_DOZING,
+ PlugState.PLUGGED);
target.assertDumpable();
assertEquals(
- "BS=0,I=0,D=0:{4m,1000,15000.00uA/H,1500.00%}\n" +
- "BS=1,I=0,D=0:{0m,0,0.00uA/H,0.00%}\n" +
- "BS=0,I=1,D=0:{14m,800,3428.57uA/H,342.86%}\n" +
- "BS=1,I=1,D=0:{9m,900,6000.00uA/H,600.00%}\n" +
- "BS=0,I=0,D=1:{5m,100,1200.00uA/H,120.00%}\n" +
- "BS=1,I=0,D=1:{0m,0,0.00uA/H,0.00%}\n" +
- "BS=0,I=1,D=1:{0m,0,0.00uA/H,0.00%}\n" +
- "BS=1,I=1,D=1:{0m,0,0.00uA/H,0.00%}\n" +
- "BS=0,I=0,D=2:{1m,200,12000.00uA/H,1200.00%}\n" +
- "BS=1,I=0,D=2:{0m,0,0.00uA/H,0.00%}\n" +
- "BS=0,I=1,D=2:{0m,0,0.00uA/H,0.00%}\n" +
- "BS=1,I=1,D=2:{0m,0,0.00uA/H,0.00%}",
+ "BS=0,I=0,D=0,P=0:{4m,1000,15000.00uA/H,1500.00%}\n"
+ + "BS=1,I=0,D=0,P=0:{0m,0,0.00uA/H,0.00%}\n"
+ + "BS=0,I=1,D=0,P=0:{14m,800,3428.57uA/H,342.86%}\n"
+ + "BS=1,I=1,D=0,P=0:{9m,900,6000.00uA/H,600.00%}\n"
+ + "BS=0,I=0,D=1,P=0:{5m,100,1200.00uA/H,120.00%}\n"
+ + "BS=1,I=0,D=1,P=0:{0m,0,0.00uA/H,0.00%}\n"
+ + "BS=0,I=1,D=1,P=0:{0m,0,0.00uA/H,0.00%}\n"
+ + "BS=1,I=1,D=1,P=0:{0m,0,0.00uA/H,0.00%}\n"
+ + "BS=0,I=0,D=2,P=0:{1m,200,12000.00uA/H,1200.00%}\n"
+ + "BS=1,I=0,D=2,P=0:{0m,0,0.00uA/H,0.00%}\n"
+ + "BS=0,I=1,D=2,P=0:{0m,0,0.00uA/H,0.00%}\n"
+ + "BS=1,I=1,D=2,P=0:{0m,0,0.00uA/H,0.00%}\n"
+ + "BS=1,I=1,D=0,P=1:{5m,1000,12000.00uA/H,1200.00%}",
target.toDebugString());
}
@@ -242,7 +263,8 @@
target.transitionState(
BatterySaverState.OFF,
InteractiveState.INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
verify(mMetricsLogger, times(0)).count(anyString(), anyInt());
@@ -253,7 +275,8 @@
target.transitionState(
BatterySaverState.OFF,
InteractiveState.NON_INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
assertLog();
@@ -264,7 +287,8 @@
target.transitionState(
BatterySaverState.OFF,
InteractiveState.NON_INTERACTIVE,
- DozeState.DEEP);
+ DozeState.DEEP,
+ PlugState.UNPLUGGED);
target.advanceClock(1);
target.drainBattery(2000);
@@ -274,7 +298,8 @@
target.transitionState(
BatterySaverState.OFF,
InteractiveState.NON_INTERACTIVE,
- DozeState.LIGHT);
+ DozeState.LIGHT,
+ PlugState.UNPLUGGED);
target.advanceClock(1);
target.drainBattery(2000);
@@ -284,7 +309,8 @@
target.transitionState(
BatterySaverState.ON,
InteractiveState.INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
assertLog();
@@ -292,7 +318,11 @@
target.drainBattery(10000);
reset(mMetricsLogger);
- target.startCharging();
+ target.transitionState(
+ BatterySaverState.ON,
+ InteractiveState.INTERACTIVE,
+ DozeState.NOT_DOZING,
+ PlugState.PLUGGED);
assertLog();
@@ -303,14 +333,19 @@
target.transitionState(
BatterySaverState.ON,
InteractiveState.NON_INTERACTIVE,
- DozeState.NOT_DOZING);
+ DozeState.NOT_DOZING,
+ PlugState.UNPLUGGED);
verify(mMetricsLogger, times(0)).count(anyString(), anyInt());
target.advanceClock(1);
target.drainBattery(2000);
- target.startCharging();
+ target.transitionState(
+ BatterySaverState.ON,
+ InteractiveState.NON_INTERACTIVE,
+ DozeState.NOT_DOZING,
+ PlugState.PLUGGED);
assertLog();
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index 971d2e2..1536345 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -21,12 +21,16 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.timezonedetector.ITimeZoneConfigurationListener;
@@ -158,10 +162,24 @@
}
}
+ @Test(expected = SecurityException.class)
+ public void testRemoveConfigurationListener_withoutPermission() {
+ doThrow(new SecurityException("Mock"))
+ .when(mMockContext).enforceCallingPermission(anyString(), any());
+
+ ITimeZoneConfigurationListener mockListener = mock(ITimeZoneConfigurationListener.class);
+ try {
+ mTimeZoneDetectorService.removeConfigurationListener(mockListener);
+ fail();
+ } finally {
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
+ anyString());
+ }
+ }
+
@Test
public void testConfigurationChangeListenerRegistrationAndCallbacks() throws Exception {
- doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
-
TimeZoneConfiguration autoDetectDisabledConfiguration =
createTimeZoneConfiguration(false /* autoDetectionEnabled */);
@@ -169,22 +187,69 @@
IBinder mockListenerBinder = mock(IBinder.class);
ITimeZoneConfigurationListener mockListener = mock(ITimeZoneConfigurationListener.class);
- when(mockListener.asBinder()).thenReturn(mockListenerBinder);
- mTimeZoneDetectorService.addConfigurationListener(mockListener);
+ {
+ doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
+ when(mockListener.asBinder()).thenReturn(mockListenerBinder);
- verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
- anyString());
- verify(mockListenerBinder).linkToDeath(any(), eq(0));
+ mTimeZoneDetectorService.addConfigurationListener(mockListener);
- // Simulate the configuration being changed and verify the mockListener was notified.
- TimeZoneConfiguration autoDetectEnabledConfiguration =
- createTimeZoneConfiguration(true /* autoDetectionEnabled */);
- mFakeTimeZoneDetectorStrategy.updateConfiguration(
- ARBITRARY_USER_ID, autoDetectEnabledConfiguration);
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
+ anyString());
+ verify(mockListener).asBinder();
+ verify(mockListenerBinder).linkToDeath(any(), anyInt());
+ verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext);
+ reset(mockListenerBinder, mockListener, mMockContext);
+ }
- verify(mockListener).onChange(autoDetectEnabledConfiguration);
+ {
+ doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
+
+ // Simulate the configuration being changed and verify the mockListener was notified.
+ TimeZoneConfiguration autoDetectEnabledConfiguration =
+ createTimeZoneConfiguration(true /* autoDetectionEnabled */);
+
+ mTimeZoneDetectorService.updateConfiguration(autoDetectEnabledConfiguration);
+
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
+ anyString());
+ verify(mockListener).onChange(autoDetectEnabledConfiguration);
+ verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext);
+ reset(mockListenerBinder, mockListener, mMockContext);
+ }
+
+ {
+ doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
+ when(mockListener.asBinder()).thenReturn(mockListenerBinder);
+ when(mockListenerBinder.unlinkToDeath(any(), anyInt())).thenReturn(true);
+
+ // Now remove the listener, change the config again, and verify the listener is not
+ // called.
+ mTimeZoneDetectorService.removeConfigurationListener(mockListener);
+
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
+ anyString());
+ verify(mockListener).asBinder();
+ verify(mockListenerBinder).unlinkToDeath(any(), eq(0));
+ verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext);
+ reset(mockListenerBinder, mockListener, mMockContext);
+ }
+
+ {
+ doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
+
+ mTimeZoneDetectorService.updateConfiguration(autoDetectDisabledConfiguration);
+
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
+ anyString());
+ verify(mockListener, never()).onChange(any());
+ verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext);
+ reset(mockListenerBinder, mockListener, mMockContext);
+ }
}
@Test(expected = SecurityException.class)
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 c2afa5f..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
@@ -1021,7 +1018,7 @@
// Note the activities have non-null ActivityRecord.app, so it won't remove directly.
mRootWindowContainer.mFinishDisabledPackageActivitiesHelper.process(
firstActivity.packageName, null /* filterByClasses */, true /* doit */,
- true /* evenPersistent */, UserHandle.USER_ALL);
+ true /* evenPersistent */, UserHandle.USER_ALL, false /* onlyRemoveNoProcess */);
// If the activity is disabled with {@link android.content.pm.PackageManager#DONT_KILL_APP}
// the activity should still follow the normal flow to finish and destroy.
@@ -1050,7 +1047,7 @@
mRootWindowContainer.mFinishDisabledPackageActivitiesHelper.process(
activity.packageName, null /* filterByClasses */, true /* doit */,
- true /* evenPersistent */, UserHandle.USER_ALL);
+ true /* evenPersistent */, UserHandle.USER_ALL, false /* onlyRemoveNoProcess */);
// Although the overlay activity is in another package, the non-overlay activities are
// removed from the task. Since the overlay activity should be removed as well, the task
@@ -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/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index f2f8a12..d07000f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -74,6 +74,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManagerInternal;
import android.graphics.Rect;
+import android.os.Binder;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
@@ -618,7 +619,7 @@
UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
false, true, false, false, false);
runAndVerifyBackgroundActivityStartsSubtest(
- "disallowed_callerIsWhitelisted_notAborted", false,
+ "disallowed_callerIsAllowed_notAborted", false,
UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
false, false, true, false, false);
@@ -639,7 +640,7 @@
int callingUid, boolean callingUidHasVisibleWindow, int callingUidProcState,
int realCallingUid, boolean realCallingUidHasVisibleWindow, int realCallingUidProcState,
boolean hasForegroundActivities, boolean callerIsRecents,
- boolean callerIsTempWhitelisted,
+ boolean callerIsTempAllowed,
boolean callerIsInstrumentingWithBackgroundActivityStartPrivileges,
boolean isCallingUidDeviceOwner) {
// window visibility
@@ -664,8 +665,10 @@
RecentTasks recentTasks = mock(RecentTasks.class);
mService.mStackSupervisor.setRecentTasks(recentTasks);
doReturn(callerIsRecents).when(recentTasks).isCallerRecents(callingUid);
- // caller is temp whitelisted
- callerApp.setAllowBackgroundActivityStarts(callerIsTempWhitelisted);
+ // caller is temp allowed
+ if (callerIsTempAllowed) {
+ callerApp.addAllowBackgroundActivityStartsToken(new Binder(), null);
+ }
// caller is instrumenting with background activity starts privileges
callerApp.setInstrumenting(callerIsInstrumentingWithBackgroundActivityStartPrivileges,
callerIsInstrumentingWithBackgroundActivityStartPrivileges);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index f97c3c9..5be2f04 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -307,7 +307,7 @@
wpc = mWpc;
} else {
wpc = new WindowProcessController(mService,
- mService.mContext.getApplicationInfo(), mProcessName, mUid,
+ aInfo.applicationInfo, mProcessName, mUid,
UserHandle.getUserId(12345), mock(Object.class),
mock(WindowProcessListener.class));
wpc.setThread(mock(IApplicationThread.class));
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index a7e0dd4..96ea646 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -974,26 +974,6 @@
}
@Test
- public void testComputeImeControlTarget_exitingApp() throws Exception {
- final DisplayContent dc = createNewDisplay();
-
- WindowState exitingWin = createWindow(null, TYPE_BASE_APPLICATION, "exiting app");
- makeWindowVisible(exitingWin);
- exitingWin.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
- exitingWin.mAnimatingExit = true;
-
- dc.mInputMethodControlTarget = exitingWin;
- dc.mInputMethodTarget = dc.mInputMethodInputTarget =
- createWindow(null, TYPE_BASE_APPLICATION, "starting app");
-
- assertEquals(exitingWin, dc.computeImeControlTarget());
-
- exitingWin.removeImmediately();
-
- assertEquals(dc.mInputMethodInputTarget, dc.computeImeControlTarget());
- }
-
- @Test
public void testComputeImeControlTarget_splitscreen() throws Exception {
final DisplayContent dc = createNewDisplay();
dc.mInputMethodInputTarget = createWindow(null, TYPE_BASE_APPLICATION, "app");
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/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 0b8db17..2e4c9ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -25,6 +25,8 @@
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.Task.ActivityState.FINISHING;
import static com.android.server.wm.Task.ActivityState.PAUSED;
import static com.android.server.wm.Task.ActivityState.PAUSING;
@@ -169,5 +171,27 @@
activity.setState(FINISHING, "test FINISHING");
assertThat(mWm.mRoot.allPausedActivitiesComplete()).isTrue();
}
+
+ @Test
+ public void testForceStopPackage() {
+ final Task task = new ActivityTestsBase.StackBuilder(mWm.mRoot).build();
+ final ActivityRecord activity1 = task.getTopMostActivity();
+ final ActivityRecord activity2 =
+ new ActivityTestsBase.ActivityBuilder(mWm.mAtmService).setStack(task).build();
+ final WindowProcessController wpc = activity1.app;
+ spyOn(wpc);
+ activity1.app = null;
+ activity2.setProcess(wpc);
+ doReturn(true).when(wpc).isRemoved();
+
+ mWm.mAtmService.mInternal.onForceStopPackage(wpc.mInfo.packageName, true /* doit */,
+ false /* evenPersistent */, wpc.mUserId);
+ // The activity without process should be removed.
+ assertEquals(1, task.getChildCount());
+
+ mWm.mRoot.handleAppDied(wpc);
+ // The activity with process should be removed because WindowProcessController#isRemoved.
+ assertFalse(task.hasChild());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
index 2233b22..ff753f2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -26,7 +26,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
import static java.util.concurrent.TimeUnit.SECONDS;
@@ -34,6 +33,7 @@
import android.animation.ValueAnimator;
import android.graphics.Matrix;
import android.graphics.Point;
+import android.hardware.power.Boost;
import android.os.Handler;
import android.os.PowerManagerInternal;
import android.platform.test.annotations.Presubmit;
@@ -202,7 +202,7 @@
mMockTransaction, this::finishedCallback);
waitUntilNextFrame();
- verify(mMockPowerManager).powerHint(anyInt(), eq(0));
+ verify(mMockPowerManager).setPowerBoost(eq(Boost.INTERACTION), eq(0));
}
private void waitUntilNextFrame() throws Exception {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 039ffc4..92b6e6e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -186,4 +186,19 @@
assertTrue(r.finishing);
});
}
+
+ @Test
+ public void testSwitchUser() {
+ final Task rootTask = createTaskStackOnDisplay(mDisplayContent);
+ final Task childTask = createTaskInStack(rootTask, 0 /* userId */);
+ final Task leafTask1 = createTaskInStack(childTask, 10 /* userId */);
+ final Task leafTask2 = createTaskInStack(childTask, 0 /* userId */);
+ assertEquals(1, rootTask.getChildCount());
+ assertEquals(leafTask2, childTask.getTopChild());
+
+ doReturn(true).when(leafTask1).showToCurrentUser();
+ rootTask.switchUser(10);
+ assertEquals(1, rootTask.getChildCount());
+ assertEquals(leafTask1, childTask.getTopChild());
+ }
}
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/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index cef202c..f185da3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -84,6 +84,10 @@
assertFalse(token.hasWindow(window12));
assertTrue(token.hasWindow(window2));
assertTrue(token.hasWindow(window3));
+
+ // The child windows should have the same window token as their parents.
+ assertEquals(window1.mToken, window11.mToken);
+ assertEquals(window1.mToken, window12.mToken);
}
@Test
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/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 070dacb..5161fba 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -28,6 +28,7 @@
"flickerlib",
"truth-prebuilt",
"app-helpers-core",
+ "launcher-helper-lib",
"launcher-aosp-tapl"
],
}
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index 58df2a8..68c99a3 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -27,7 +27,7 @@
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="com.android.server.wm.flicker"/>
- <option name="exclude-annotation" value="org.junit.Ignore" />
+ <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
<option name="shell-timeout" value="6600s" />
<option name="test-timeout" value="6000s" />
<option name="hidden-api-checks" value="false" />
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.kt
index f871ea5..43cfdff 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.kt
@@ -20,22 +20,24 @@
import android.util.Rational
import android.view.Surface
import androidx.test.InstrumentationRegistry
+import androidx.test.filters.FlakyTest
import androidx.test.filters.LargeTest
import androidx.test.runner.AndroidJUnit4
import androidx.test.uiautomator.UiDevice
import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.helpers.PipAppHelper
import org.junit.FixMethodOrder
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
/**
* Tests to help debug individual transitions, capture video recordings and create test cases.
+ *
+ * Not actual tests
*/
@LargeTest
-@Ignore("Used for debugging transitions used in FlickerTests.")
+@FlakyTest
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class DebugTest {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt
index 653fecd..1f8150c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt
@@ -18,7 +18,6 @@
import android.view.Surface
import androidx.test.filters.FlakyTest
-import org.junit.Ignore
import org.junit.Test
import org.junit.runners.Parameterized
@@ -27,7 +26,6 @@
protected val beginRotation: Int
) : FlickerTestBase() {
@FlakyTest(bugId = 141361128)
- @Ignore("Waiting bug feedback")
@Test
fun checkCoveredRegion_noUncoveredRegions() {
val displayBounds = WindowUtils.getDisplayBounds(beginRotation)
@@ -38,7 +36,6 @@
}
@FlakyTest(bugId = 141361128)
- @Ignore("Waiting bug feedback")
@Test
fun checkVisibility_navBarLayerIsAlwaysVisible() {
checkResults {
@@ -48,7 +45,6 @@
}
@FlakyTest(bugId = 141361128)
- @Ignore("Waiting bug feedback")
@Test
fun checkVisibility_statusBarLayerIsAlwaysVisible() {
checkResults {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt
index 873d607..dfc3c07 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt
@@ -18,7 +18,6 @@
import android.view.Surface
import androidx.test.filters.FlakyTest
-import org.junit.Ignore
import org.junit.Test
import org.junit.runners.Parameterized
@@ -29,7 +28,6 @@
protected val endRotation: Int
) : FlickerTestBase() {
@FlakyTest(bugId = 140855415)
- @Ignore("Waiting bug feedback")
@Test
fun checkVisibility_navBarWindowIsAlwaysVisible() {
checkResults {
@@ -39,7 +37,6 @@
}
@FlakyTest(bugId = 140855415)
- @Ignore("Waiting bug feedback")
@Test
fun checkVisibility_statusBarWindowIsAlwaysVisible() {
checkResults {
@@ -86,7 +83,6 @@
}
@FlakyTest(bugId = 140855415)
- @Ignore("Waiting bug feedback")
@Test
fun checkVisibility_navBarLayerIsAlwaysVisible() {
checkResults {
@@ -96,7 +92,6 @@
}
@FlakyTest(bugId = 140855415)
- @Ignore("Waiting bug feedback")
@Test
fun checkVisibility_statusBarLayerIsAlwaysVisible() {
checkResults {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index 6946618..814cdcf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -22,7 +22,6 @@
import com.android.server.wm.flicker.TransitionRunner
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
import org.junit.FixMethodOrder
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -49,21 +48,18 @@
.includeJankyRuns().build()
@FlakyTest(bugId = 141458352)
- @Ignore("Waiting bug feedback")
@Test
override fun checkVisibility_imeLayerBecomesInvisible() {
super.checkVisibility_imeLayerBecomesInvisible()
}
@FlakyTest(bugId = 141458352)
- @Ignore("Waiting bug feedback")
@Test
override fun checkVisibility_imeAppLayerIsAlwaysVisible() {
super.checkVisibility_imeAppLayerIsAlwaysVisible()
}
@FlakyTest(bugId = 141458352)
- @Ignore("Waiting bug feedback")
@Test
override fun checkVisibility_imeAppWindowIsAlwaysVisible() {
super.checkVisibility_imeAppWindowIsAlwaysVisible()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index a1030d8..c2025b6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -22,7 +22,6 @@
import com.android.server.wm.flicker.TransitionRunner
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
import org.junit.FixMethodOrder
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -49,21 +48,18 @@
.includeJankyRuns().build()
@FlakyTest(bugId = 141458352)
- @Ignore("Waiting bug feedback")
@Test
override fun checkVisibility_imeWindowBecomesInvisible() {
super.checkVisibility_imeWindowBecomesInvisible()
}
@FlakyTest(bugId = 141458352)
- @Ignore("Waiting bug feedback")
@Test
override fun checkVisibility_imeLayerBecomesInvisible() {
super.checkVisibility_imeLayerBecomesInvisible()
}
@FlakyTest(bugId = 157449248)
- @Ignore("Waiting bug feedback")
@Test
override fun checkVisibility_imeAppWindowBecomesInvisible() {
super.checkVisibility_imeAppWindowBecomesInvisible()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 6e7c92b..b38262e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.ime
+import androidx.test.filters.FlakyTest
import androidx.test.filters.LargeTest
import com.android.server.wm.flicker.CommonTransitions
import com.android.server.wm.flicker.LayersTraceSubject
@@ -24,7 +25,6 @@
import com.android.server.wm.flicker.WmTraceSubject
import com.android.server.wm.flicker.helpers.ImeAppHelper
import org.junit.FixMethodOrder
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -50,7 +50,7 @@
instrumentation, uiDevice, beginRotation)
.includeJankyRuns().build()
- @Ignore("Flaky. Pending debug")
+ @FlakyTest
@Test
open fun checkVisibility_imeLayerBecomesInvisible() {
checkResults {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index ed8bd2a..ca04bab 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -25,7 +25,6 @@
import com.android.server.wm.flicker.WmTraceSubject
import com.android.server.wm.flicker.helpers.ImeAppHelper
import org.junit.FixMethodOrder
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -63,7 +62,6 @@
}
@FlakyTest(bugId = 153739621)
- @Ignore
@Test
open fun checkVisibility_imeLayerBecomesInvisible() {
checkResults {
@@ -77,7 +75,6 @@
}
@FlakyTest(bugId = 153739621)
- @Ignore
@Test
fun checkVisibility_imeAppLayerBecomesInvisible() {
checkResults {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 943a525..88b8854 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -25,7 +25,6 @@
import com.android.server.wm.flicker.TransitionRunner
import com.android.server.wm.flicker.WmTraceSubject
import org.junit.FixMethodOrder
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -63,7 +62,6 @@
}
@FlakyTest(bugId = 140855415)
- @Ignore("Waiting bug feedback")
@Test
fun checkZOrder_appWindowReplacesLauncherAsTopWindow() {
checkResults {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index 7964d94..f0bc3f0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -25,7 +25,6 @@
import com.android.server.wm.flicker.TransitionRunner
import com.android.server.wm.flicker.WmTraceSubject
import org.junit.FixMethodOrder
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -63,7 +62,6 @@
}
@FlakyTest(bugId = 140855415)
- @Ignore("Waiting bug feedback")
@Test
fun checkZOrder_appWindowReplacesLauncherAsTopWindow() {
checkResults {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt
index f939a79..89ffb7a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt
@@ -23,7 +23,6 @@
import com.android.server.wm.flicker.WmTraceSubject
import com.android.server.wm.flicker.helpers.PipAppHelper
import org.junit.FixMethodOrder
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -37,7 +36,6 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 152738416)
-@Ignore("Waiting bug feedback")
class PipToAppTest(
beginRotationName: String,
beginRotation: Int
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt
index ecfcd82..8591360 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt
@@ -23,7 +23,6 @@
import com.android.server.wm.flicker.WmTraceSubject
import com.android.server.wm.flicker.helpers.PipAppHelper
import org.junit.FixMethodOrder
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -37,7 +36,6 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 152738416)
-@Ignore("Waiting bug feedback")
class PipToHomeTest(
beginRotationName: String,
beginRotation: Int
@@ -47,7 +45,6 @@
uiDevice, beginRotation)
.includeJankyRuns().build()
- @Ignore
@Test
fun checkVisibility_backgroundWindowVisibleBehindPipLayer() {
checkResults {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 7a581d0..fb1cb39 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -17,6 +17,7 @@
package com.android.server.wm.flicker.rotation
import android.util.Log
+import androidx.test.filters.FlakyTest
import androidx.test.filters.LargeTest
import com.android.server.wm.flicker.CommonTransitions
import com.android.server.wm.flicker.LayersTraceSubject
@@ -25,7 +26,6 @@
import com.android.server.wm.flicker.TransitionRunner
import com.android.server.wm.flicker.WindowUtils
import org.junit.FixMethodOrder
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -67,7 +67,7 @@
}
}
- @Ignore("Flaky. Pending debug")
+ @FlakyTest
@Test
fun checkVisibility_screenshotLayerBecomesInvisible() {
checkResults {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index d53af6f..1cd1998 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -28,7 +28,6 @@
import com.android.server.wm.flicker.WindowUtils
import com.android.server.wm.flicker.testapp.ActivityOptions
import org.junit.FixMethodOrder
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -42,7 +41,6 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 147659548)
-@Ignore("Waiting bug feedback")
class SeamlessAppRotationTest(
private val intent: Intent,
beginRotationName: String,
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
index b6cce26..6b597e5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
@@ -38,7 +38,6 @@
import com.google.common.truth.Truth
import org.junit.AfterClass
import org.junit.FixMethodOrder
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -53,7 +52,6 @@
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 159096424)
-@Ignore("Waiting bug feedback")
class ResizeSplitScreenTest : FlickerTestBase() {
init {
testApp = StandardAppHelper(instrumentation,
@@ -97,7 +95,7 @@
}
@Test
- @Ignore("Waiting feedback")
+ @FlakyTest
fun checkPosition_appsStartingBounds() {
val displayBounds = WindowUtils.getDisplayBounds()
checkResults { result: TransitionResult ->
@@ -122,7 +120,7 @@
}
@Test
- @Ignore("Waiting feedback")
+ @FlakyTest
fun checkPosition_appsEndingBounds() {
val displayBounds = WindowUtils.getDisplayBounds()
checkResults { result: TransitionResult ->
@@ -166,7 +164,6 @@
@Test
@FlakyTest(bugId = 156223549)
- @Ignore("Waiting bug feedback")
fun checkVisibility_topAppWindowIsAlwaysVisible() {
checkResults {
WmTraceSubject.assertThat(it)
@@ -177,7 +174,6 @@
@Test
@FlakyTest(bugId = 156223549)
- @Ignore("Waiting bug feedback")
fun checkVisibility_bottomAppWindowIsAlwaysVisible() {
checkResults {
WmTraceSubject.assertThat(it)
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/tests/WindowInsetsTests/AndroidManifest.xml b/tests/WindowInsetsTests/AndroidManifest.xml
index 5978054..61dd9d4 100644
--- a/tests/WindowInsetsTests/AndroidManifest.xml
+++ b/tests/WindowInsetsTests/AndroidManifest.xml
@@ -16,18 +16,24 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.google.android.test.windowinsetstests">
+ package="com.google.android.test.windowinsetstests">
- <application android:label="@string/activity_title">
- <activity android:name=".WindowInsetsActivity"
- android:theme="@style/appTheme"
- android:windowSoftInputMode="adjustResize"
- android:exported="true">
-
+ <application android:label="@string/application_title">
+ <activity android:name=".WindowInsetsTestsMainActivity"
+ android:exported="true">
<intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+
+ <activity android:name=".ChatActivity"
+ android:label="@string/chat_activity_title"
+ android:theme="@style/chat"
+ android:windowSoftInputMode="adjustResize" />
+
+ <activity android:name=".ControllerActivity"
+ android:label="@string/controller_activity_title"
+ android:theme="@style/controller" />
</application>
</manifest>
diff --git a/tests/WindowInsetsTests/res/layout/window_inset_activity.xml b/tests/WindowInsetsTests/res/layout/chat_activity.xml
similarity index 100%
rename from tests/WindowInsetsTests/res/layout/window_inset_activity.xml
rename to tests/WindowInsetsTests/res/layout/chat_activity.xml
diff --git a/tests/WindowInsetsTests/res/layout/controller_activity.xml b/tests/WindowInsetsTests/res/layout/controller_activity.xml
new file mode 100644
index 0000000..d51a4dd
--- /dev/null
+++ b/tests/WindowInsetsTests/res/layout/controller_activity.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <Spinner
+ android:id="@+id/spinnerBehavior"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="20dp" />
+
+ <ToggleButton
+ android:id="@+id/toggleButtonStatus"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:checked="true"
+ android:text="Status Bars Toggle Button"
+ android:textOff="Status Bars Invisible"
+ android:textOn="Status Bars Visible" />
+
+ <SeekBar
+ android:id="@+id/seekBarStatus"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="20dp"
+ android:max="10000"
+ android:progress="10000" />
+
+ <ToggleButton
+ android:id="@+id/toggleButtonNavigation"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:checked="true"
+ android:text="Navigation Bars Toggle Button"
+ android:textOff="Navigation Bars Invisible"
+ android:textOn="Navigation Bars Visible" />
+
+ <SeekBar
+ android:id="@+id/seekBarNavigation"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="20dp"
+ android:max="10000"
+ android:progress="10000" />
+
+ <ToggleButton
+ android:id="@+id/toggleButtonIme"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:checked="true"
+ android:text="IME Toggle Button"
+ android:textOff="IME Invisible"
+ android:textOn="IME Visible" />
+
+ <SeekBar
+ android:id="@+id/seekBarIme"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="20dp"
+ android:max="10000"
+ android:progress="0" />
+
+ <TextView
+ android:id="@+id/textViewControllableInsets"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dp" />
+
+ <EditText
+ android:id="@+id/editText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ems="10"
+ android:hint="For testing IME..."
+ android:inputType="text"
+ android:text="" />
+
+ </LinearLayout>
+
+</ScrollView>
\ No newline at end of file
diff --git a/tests/WindowInsetsTests/res/layout/main_activity.xml b/tests/WindowInsetsTests/res/layout/main_activity.xml
new file mode 100644
index 0000000..621ed89
--- /dev/null
+++ b/tests/WindowInsetsTests/res/layout/main_activity.xml
@@ -0,0 +1,37 @@
+<?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"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <Button
+ android:id="@+id/chat_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/chat_activity_title"
+ android:textAllCaps="false"/>
+
+ <Button
+ android:id="@+id/controller_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/controller_activity_title"
+ android:textAllCaps="false"/>
+
+</LinearLayout>
diff --git a/tests/WindowInsetsTests/res/values/strings.xml b/tests/WindowInsetsTests/res/values/strings.xml
index 2b8e5f3d..1a236c6 100644
--- a/tests/WindowInsetsTests/res/values/strings.xml
+++ b/tests/WindowInsetsTests/res/values/strings.xml
@@ -16,5 +16,14 @@
-->
<resources>
- <string name="activity_title">New Insets Chat</string>
+ <string name="application_title">Window Insets Tests</string>
+ <string name="chat_activity_title">New Insets Chat</string>
+ <string name="controller_activity_title">Window Insets Controller</string>
+
+ <!-- The item positions should match the flag values respectively. -->
+ <string-array name="behaviors">
+ <item>BEHAVIOR_SHOW_BARS_BY_TOUCH</item>
+ <item>BEHAVIOR_SHOW_BARS_BY_SWIPE</item>
+ <item>BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE</item>
+ </string-array>
</resources>
diff --git a/tests/WindowInsetsTests/res/values/styles.xml b/tests/WindowInsetsTests/res/values/styles.xml
index 220671f..a84ffbed 100644
--- a/tests/WindowInsetsTests/res/values/styles.xml
+++ b/tests/WindowInsetsTests/res/values/styles.xml
@@ -17,7 +17,7 @@
<resources>
- <style name="appTheme" parent="@style/Theme.MaterialComponents.Light">
+ <style name="chat" parent="@style/Theme.MaterialComponents.Light">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
@@ -63,5 +63,11 @@
<dimen name="bubble_padding">8dp</dimen>
<dimen name="bubble_padding_side">16dp</dimen>
+ <style name="controller" parent="android:Theme.Material">
+ <item name="android:colorPrimaryDark">#111111</item>
+ <item name="android:navigationBarColor">#111111</item>
+ <item name="android:colorPrimary">#222222</item>
+ <item name="android:colorAccent">#33ccff</item>
+ </style>
</resources>
\ No newline at end of file
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java
similarity index 96%
rename from tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
rename to tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java
index 498cb7c..ba12acb 100644
--- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java
@@ -30,7 +30,6 @@
import android.graphics.Insets;
import android.os.Bundle;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@@ -39,8 +38,6 @@
import android.view.WindowInsetsAnimation.Callback;
import android.view.WindowInsetsAnimationControlListener;
import android.view.WindowInsetsAnimationController;
-import android.view.WindowInsetsController;
-import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
import android.view.animation.LinearInterpolator;
import android.widget.LinearLayout;
@@ -49,7 +46,7 @@
import androidx.appcompat.app.AppCompatActivity;
-public class WindowInsetsActivity extends AppCompatActivity {
+public class ChatActivity extends AppCompatActivity {
private View mRoot;
@@ -58,7 +55,7 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.window_inset_activity);
+ setContentView(R.layout.chat_activity);
setSupportActionBar(findViewById(R.id.toolbar));
@@ -71,7 +68,7 @@
mRoot.setOnTouchListener(new View.OnTouchListener() {
private final ViewConfiguration mViewConfiguration =
- ViewConfiguration.get(WindowInsetsActivity.this);
+ ViewConfiguration.get(ChatActivity.this);
WindowInsetsAnimationController mAnimationController;
WindowInsetsAnimationControlListener mCurrentRequest;
boolean mRequestedController = false;
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java
new file mode 100644
index 0000000..beb4049
--- /dev/null
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java
@@ -0,0 +1,202 @@
+/*
+ * 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.google.android.test.windowinsetstests;
+
+import android.app.Activity;
+import android.graphics.Insets;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowInsets.Type;
+import android.view.WindowInsetsAnimationControlListener;
+import android.view.WindowInsetsAnimationController;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.CompoundButton;
+import android.widget.SeekBar;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+public class ControllerActivity extends Activity implements View.OnApplyWindowInsetsListener {
+
+ private ToggleButton mToggleStatus;
+ private SeekBar mSeekStatus;
+ private ToggleButton mToggleNavigation;
+ private SeekBar mSeekNavigation;
+ private ToggleButton mToggleIme;
+ private SeekBar mSeekIme;
+ private TextView mTextControllableInsets;
+ private boolean[] mNotFromUser = {false};
+ private WindowInsets mLastInsets;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.controller_activity);
+ final Spinner spinnerBehavior = findViewById(R.id.spinnerBehavior);
+ ArrayAdapter<CharSequence> adapterBehavior = ArrayAdapter.createFromResource(this,
+ R.array.behaviors, android.R.layout.simple_spinner_item);
+ adapterBehavior.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ spinnerBehavior.setAdapter(adapterBehavior);
+ spinnerBehavior.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ parent.getWindowInsetsController().setSystemBarsBehavior(position);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) { }
+ });
+ mToggleStatus = findViewById(R.id.toggleButtonStatus);
+ mToggleStatus.setTag(mNotFromUser);
+ mToggleStatus.setOnCheckedChangeListener(new ToggleListener(Type.statusBars()));
+ mSeekStatus = findViewById(R.id.seekBarStatus);
+ mSeekStatus.setOnSeekBarChangeListener(new SeekBarListener(Type.statusBars()));
+ mToggleNavigation = findViewById(R.id.toggleButtonNavigation);
+ mToggleNavigation.setTag(mNotFromUser);
+ mToggleNavigation.setOnCheckedChangeListener(new ToggleListener(Type.navigationBars()));
+ mSeekNavigation = findViewById(R.id.seekBarNavigation);
+ mSeekNavigation.setOnSeekBarChangeListener(new SeekBarListener(Type.navigationBars()));
+ mToggleIme = findViewById(R.id.toggleButtonIme);
+ mToggleIme.setTag(mNotFromUser);
+ mToggleIme.setOnCheckedChangeListener(new ToggleListener(Type.ime()));
+ mSeekIme = findViewById(R.id.seekBarIme);
+ mSeekIme.setOnSeekBarChangeListener(new SeekBarListener(Type.ime()));
+ mTextControllableInsets = findViewById(R.id.textViewControllableInsets);
+ final View contentView = findViewById(R.id.content);
+ contentView.setOnApplyWindowInsetsListener(this);
+ contentView.getWindowInsetsController().addOnControllableInsetsChangedListener(
+ (c, types) -> mTextControllableInsets.setText("ControllableInsetsTypes=" + types));
+ }
+
+ @Override
+ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+ mNotFromUser[0] = true;
+ updateWidgets(insets, Type.statusBars(), mToggleStatus, mSeekStatus);
+ updateWidgets(insets, Type.navigationBars(), mToggleNavigation, mSeekNavigation);
+ updateWidgets(insets, Type.ime(), mToggleIme, mSeekIme);
+ mLastInsets = insets;
+ mNotFromUser[0] = false;
+
+ // Prevent triggering system gestures while controlling seek bars.
+ final Insets gestureInsets = insets.getInsets(Type.systemGestures());
+ v.setPadding(gestureInsets.left, 0, gestureInsets.right, 0);
+
+ return v.onApplyWindowInsets(insets);
+ }
+
+ private void updateWidgets(WindowInsets insets, int types, ToggleButton toggle, SeekBar seek) {
+ final boolean isVisible = insets.isVisible(types);
+ final boolean wasVisible = mLastInsets != null ? mLastInsets.isVisible(types) : !isVisible;
+ if (isVisible != wasVisible) {
+ toggle.setChecked(isVisible);
+ if (!seek.isPressed()) {
+ seek.setProgress(isVisible ? seek.getMax() : seek.getMin(), true /* animate*/);
+ }
+ }
+
+ }
+
+ private static class ToggleListener implements CompoundButton.OnCheckedChangeListener {
+
+ private final @Type.InsetsType int mTypes;
+
+ ToggleListener(int types) {
+ mTypes = types;
+ }
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (((boolean[]) buttonView.getTag())[0]) {
+ // not from user
+ return;
+ }
+ if (isChecked) {
+ buttonView.getWindowInsetsController().show(mTypes);
+ } else {
+ buttonView.getWindowInsetsController().hide(mTypes);
+ }
+ }
+ }
+
+ private static class SeekBarListener implements SeekBar.OnSeekBarChangeListener {
+
+ private final @Type.InsetsType int mTypes;
+
+ private WindowInsetsAnimationController mController;
+
+ SeekBarListener(int types) {
+ mTypes = types;
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (mController != null && fromUser) {
+ final int min = seekBar.getMin();
+ final float fraction = (progress - min) / (float) (seekBar.getMax() - min);
+ final Insets shownInsets = mController.getShownStateInsets();
+ final Insets hiddenInsets = mController.getHiddenStateInsets();
+ final Insets currentInsets = Insets.of(
+ (int) (0.5f + fraction * (shownInsets.left - hiddenInsets.left)),
+ (int) (0.5f + fraction * (shownInsets.top - hiddenInsets.top)),
+ (int) (0.5f + fraction * (shownInsets.right - hiddenInsets.right)),
+ (int) (0.5f + fraction * (shownInsets.bottom - hiddenInsets.bottom)));
+ mController.setInsetsAndAlpha(currentInsets, 1f /* alpha */, fraction);
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ if (mController != null) {
+ return;
+ }
+ seekBar.getWindowInsetsController().controlWindowInsetsAnimation(mTypes,
+ -1 /* durationMs */, null /* interpolator */, null /* cancellationSignal */,
+ new WindowInsetsAnimationControlListener() {
+ @Override
+ public void onReady(WindowInsetsAnimationController controller, int types) {
+ mController = controller;
+ if (!seekBar.isPressed()) {
+ onStopTrackingTouch(seekBar);
+ }
+ }
+
+ @Override
+ public void onFinished(WindowInsetsAnimationController controller) {
+ mController = null;
+ }
+
+ @Override
+ public void onCancelled(WindowInsetsAnimationController controller) {
+ mController = null;
+ }
+ });
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ final int min = seekBar.getMin();
+ final int max = seekBar.getMax();
+ final boolean shown = (seekBar.getProgress() - min) * 2 > max - min;
+ seekBar.setProgress(shown ? max : min);
+ if (mController != null) {
+ mController.finish(shown);
+ }
+ }
+ }
+}
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsTestsMainActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsTestsMainActivity.java
new file mode 100644
index 0000000..8b77a78
--- /dev/null
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsTestsMainActivity.java
@@ -0,0 +1,36 @@
+/*
+ * 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.google.android.test.windowinsetstests;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class WindowInsetsTestsMainActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main_activity);
+
+ findViewById(R.id.chat_button).setOnClickListener(
+ v -> startActivity(new Intent(this, ChatActivity.class)));
+
+ findViewById(R.id.controller_button).setOnClickListener(
+ v -> startActivity(new Intent(this, ControllerActivity.class)));
+ }
+}
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"];