Merge "Add hint to indicate 'see more' type content"
diff --git a/Android.bp b/Android.bp
index b9a8dec..ae4f974 100644
--- a/Android.bp
+++ b/Android.bp
@@ -324,6 +324,8 @@
"core/java/android/view/IOnKeyguardExitResult.aidl",
"core/java/android/view/IPinnedStackController.aidl",
"core/java/android/view/IPinnedStackListener.aidl",
+ "core/java/android/view/IRemoteAnimationRunner.aidl",
+ "core/java/android/view/IRemoteAnimationFinishedCallback.aidl",
"core/java/android/view/IRotationWatcher.aidl",
"core/java/android/view/IWallpaperVisibilityListener.aidl",
"core/java/android/view/IWindow.aidl",
@@ -464,6 +466,8 @@
"telecomm/java/com/android/internal/telecom/IInCallService.aidl",
"telecomm/java/com/android/internal/telecom/ITelecomService.aidl",
"telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl",
+ "telephony/java/android/telephony/data/IDataService.aidl",
+ "telephony/java/android/telephony/data/IDataServiceCallback.aidl",
"telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl",
"telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl",
"telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl",
@@ -471,8 +475,6 @@
"telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl",
"telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl",
"telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl",
- "telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl",
- "telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl",
"telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl",
"telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl",
"telephony/java/android/telephony/ims/internal/aidl/IImsSmsListener.aidl",
@@ -492,6 +494,8 @@
"telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl",
"telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl",
"telephony/java/com/android/ims/internal/IImsMultiEndpoint.aidl",
+ "telephony/java/com/android/ims/internal/IImsRegistration.aidl",
+ "telephony/java/com/android/ims/internal/IImsRegistrationCallback.aidl",
"telephony/java/com/android/ims/internal/IImsRcsFeature.aidl",
"telephony/java/com/android/ims/internal/IImsService.aidl",
"telephony/java/com/android/ims/internal/IImsServiceController.aidl",
@@ -519,7 +523,30 @@
"telephony/java/com/android/internal/telephony/ITelephony.aidl",
"telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl",
"telephony/java/com/android/internal/telephony/IWapPushManager.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IDeleteProfileCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IDisableProfileCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl",
"telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IGetAllProfilesCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IGetDefaultSmdpAddressCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IGetProfileCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IGetSmdsAddressCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IResetMemoryCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl",
"wifi/java/android/net/wifi/IWifiManager.aidl",
"wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl",
"wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl",
@@ -618,6 +645,7 @@
"android.hardware.vibrator-V1.0-java-constants",
"android.hardware.vibrator-V1.1-java-constants",
"android.hardware.wifi-V1.0-java-constants",
+ "android.hardware.radio-V1.0-java",
],
// Loaded with System.loadLibrary by android.view.textclassifier
diff --git a/Android.mk b/Android.mk
index 8199c57..73c1ccd 100644
--- a/Android.mk
+++ b/Android.mk
@@ -830,3 +830,4 @@
endif
endif # ANDROID_BUILD_EMBEDDED
+
diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml
index 4889941..bd0b944 100644
--- a/apct-tests/perftests/core/AndroidManifest.xml
+++ b/apct-tests/perftests/core/AndroidManifest.xml
@@ -2,6 +2,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.perftests.core">
+ <permission android:name="com.android.perftests.core.TestPermission" />
+ <uses-permission android:name="com.android.perftests.core.TestPermission" />
+
<uses-permission
android:name="android.permission.GET_ACCOUNTS" />
diff --git a/apct-tests/perftests/core/src/android/os/PermissionTest.java b/apct-tests/perftests/core/src/android/os/PermissionTest.java
new file mode 100644
index 0000000..d292e7d
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/PermissionTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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.os;
+
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.content.Context;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class PermissionTest {
+ private static final String PERMISSION_HAS_NAME = "com.android.perftests.core.TestPermission";
+ private static final String PERMISSION_DOESNT_HAVE_NAME =
+ "com.android.perftests.core.TestBadPermission";
+ private static final String THIS_PACKAGE_NAME = "com.android.perftests.core";
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void testHasPermission() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final Context context = InstrumentationRegistry.getTargetContext();
+ while (state.keepRunning()) {
+ int ret = context.getPackageManager().checkPermission(PERMISSION_HAS_NAME,
+ THIS_PACKAGE_NAME);
+ }
+ }
+
+ @Test
+ public void testDoesntHavePermission() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final Context context = InstrumentationRegistry.getTargetContext();
+
+ while (state.keepRunning()) {
+ int ret = context.getPackageManager().checkPermission(PERMISSION_DOESNT_HAVE_NAME,
+ THIS_PACKAGE_NAME);
+ }
+ }
+
+}
diff --git a/apct-tests/perftests/core/src/android/os/PssPerfTest.java b/apct-tests/perftests/core/src/android/os/PssPerfTest.java
new file mode 100644
index 0000000..400115d
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/PssPerfTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.os;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class PssPerfTest {
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void testPss() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Debug.getPss();
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index 5653a03..93a0fc3 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -190,7 +190,7 @@
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final PremeasuredText text = PremeasuredText.build(
+ final MeasuredText text = MeasuredText.build(
generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR);
state.resumeTiming();
@@ -206,7 +206,7 @@
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final PremeasuredText text = PremeasuredText.build(
+ final MeasuredText text = MeasuredText.build(
generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR);
state.resumeTiming();
@@ -222,7 +222,7 @@
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final PremeasuredText text = PremeasuredText.build(
+ final MeasuredText text = MeasuredText.build(
generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR);
state.resumeTiming();
@@ -238,7 +238,7 @@
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final PremeasuredText text = PremeasuredText.build(
+ final MeasuredText text = MeasuredText.build(
generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR);
state.resumeTiming();
@@ -254,7 +254,7 @@
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final PremeasuredText text = PremeasuredText.build(
+ final MeasuredText text = MeasuredText.build(
generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT, LTR);
state.resumeTiming();
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
index bb9dc4a..da17818 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
@@ -25,7 +25,6 @@
import java.io.File;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.concurrent.TimeUnit;
/**
@@ -78,10 +77,7 @@
// Statistics. These values will be filled when the benchmark has finished.
// The computation needs double precision, but long int is fine for final reporting.
- private long mMedian = 0;
- private double mMean = 0.0;
- private double mStandardDeviation = 0.0;
- private long mMin = 0;
+ private Stats mStats;
// Individual duration in nano seconds.
private ArrayList<Long> mResults = new ArrayList<>();
@@ -90,36 +86,6 @@
return TimeUnit.MILLISECONDS.toNanos(ms);
}
- /**
- * Calculates statistics.
- */
- private void calculateSatistics() {
- final int size = mResults.size();
- if (size <= 1) {
- throw new IllegalStateException("At least two results are necessary.");
- }
-
- Collections.sort(mResults);
- mMedian = size % 2 == 0 ? (mResults.get(size / 2) + mResults.get(size / 2 + 1)) / 2 :
- mResults.get(size / 2);
-
- mMin = mResults.get(0);
- for (int i = 0; i < size; ++i) {
- long result = mResults.get(i);
- mMean += result;
- if (result < mMin) {
- mMin = result;
- }
- }
- mMean /= (double) size;
-
- for (int i = 0; i < size; ++i) {
- final double tmp = mResults.get(i) - mMean;
- mStandardDeviation += tmp * tmp;
- }
- mStandardDeviation = Math.sqrt(mStandardDeviation / (double) (size - 1));
- }
-
// Stops the benchmark timer.
// This method can be called only when the timer is running.
public void pauseTiming() {
@@ -173,7 +139,7 @@
if (ENABLE_PROFILING) {
Debug.stopMethodTracing();
}
- calculateSatistics();
+ mStats = new Stats(mResults);
mState = FINISHED;
return false;
}
@@ -224,28 +190,28 @@
if (mState != FINISHED) {
throw new IllegalStateException("The benchmark hasn't finished");
}
- return (long) mMean;
+ return (long) mStats.getMean();
}
private long median() {
if (mState != FINISHED) {
throw new IllegalStateException("The benchmark hasn't finished");
}
- return mMedian;
+ return mStats.getMedian();
}
private long min() {
if (mState != FINISHED) {
throw new IllegalStateException("The benchmark hasn't finished");
}
- return mMin;
+ return mStats.getMin();
}
private long standardDeviation() {
if (mState != FINISHED) {
throw new IllegalStateException("The benchmark hasn't finished");
}
- return (long) mStandardDeviation;
+ return (long) mStats.getStandardDeviation();
}
private String summaryLine() {
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java
new file mode 100644
index 0000000..2c84db1
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.perftests.utils;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Provides a benchmark framework.
+ *
+ * This differs from BenchmarkState in that rather than the class measuring the the elapsed time,
+ * the test passes in the elapsed time.
+ *
+ * Example usage:
+ *
+ * public void sampleMethod() {
+ * ManualBenchmarkState state = new ManualBenchmarkState();
+ *
+ * int[] src = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ * long elapsedTime = 0;
+ * while (state.keepRunning(elapsedTime)) {
+ * long startTime = System.nanoTime();
+ * int[] dest = new int[src.length];
+ * System.arraycopy(src, 0, dest, 0, src.length);
+ * elapsedTime = System.nanoTime() - startTime;
+ * }
+ * System.out.println(state.summaryLine());
+ * }
+ *
+ * Or use the PerfManualStatusReporter TestRule.
+ *
+ * Make sure that the overhead of checking the clock does not noticeably affect the results.
+ */
+public final class ManualBenchmarkState {
+ private static final String TAG = ManualBenchmarkState.class.getSimpleName();
+
+ // TODO: Tune these values.
+ // warm-up for duration
+ private static final long WARMUP_DURATION_NS = TimeUnit.SECONDS.toNanos(5);
+ // minimum iterations to warm-up for
+ private static final int WARMUP_MIN_ITERATIONS = 8;
+
+ // target testing for duration
+ private static final long TARGET_TEST_DURATION_NS = TimeUnit.SECONDS.toNanos(16);
+ private static final int MAX_TEST_ITERATIONS = 1000000;
+ private static final int MIN_TEST_ITERATIONS = 10;
+
+ private static final int NOT_STARTED = 0; // The benchmark has not started yet.
+ private static final int WARMUP = 1; // The benchmark is warming up.
+ private static final int RUNNING = 2; // The benchmark is running.
+ private static final int FINISHED = 3; // The benchmark has stopped.
+
+ private int mState = NOT_STARTED; // Current benchmark state.
+
+ private long mWarmupStartTime = 0;
+ private int mWarmupIterations = 0;
+
+ private int mMaxIterations = 0;
+
+ // Individual duration in nano seconds.
+ private ArrayList<Long> mResults = new ArrayList<>();
+
+ // Statistics. These values will be filled when the benchmark has finished.
+ // The computation needs double precision, but long int is fine for final reporting.
+ private Stats mStats;
+
+ private void beginBenchmark(long warmupDuration, int iterations) {
+ mMaxIterations = (int) (TARGET_TEST_DURATION_NS / (warmupDuration / iterations));
+ mMaxIterations = Math.min(MAX_TEST_ITERATIONS,
+ Math.max(mMaxIterations, MIN_TEST_ITERATIONS));
+ mState = RUNNING;
+ }
+
+ /**
+ * Judges whether the benchmark needs more samples.
+ *
+ * For the usage, see class comment.
+ */
+ public boolean keepRunning(long duration) {
+ if (duration < 0) {
+ throw new RuntimeException("duration is negative: " + duration);
+ }
+ switch (mState) {
+ case NOT_STARTED:
+ mState = WARMUP;
+ mWarmupStartTime = System.nanoTime();
+ return true;
+ case WARMUP: {
+ final long timeSinceStartingWarmup = System.nanoTime() - mWarmupStartTime;
+ ++mWarmupIterations;
+ if (mWarmupIterations >= WARMUP_MIN_ITERATIONS
+ && timeSinceStartingWarmup >= WARMUP_DURATION_NS) {
+ beginBenchmark(timeSinceStartingWarmup, mWarmupIterations);
+ }
+ return true;
+ }
+ case RUNNING: {
+ mResults.add(duration);
+ final boolean keepRunning = mResults.size() < mMaxIterations;
+ if (!keepRunning) {
+ mStats = new Stats(mResults);
+ mState = FINISHED;
+ }
+ return keepRunning;
+ }
+ case FINISHED:
+ throw new IllegalStateException("The benchmark has finished.");
+ default:
+ throw new IllegalStateException("The benchmark is in an unknown state.");
+ }
+ }
+
+ private String summaryLine() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("Summary: ");
+ sb.append("median=").append(mStats.getMedian()).append("ns, ");
+ sb.append("mean=").append(mStats.getMean()).append("ns, ");
+ sb.append("min=").append(mStats.getMin()).append("ns, ");
+ sb.append("max=").append(mStats.getMax()).append("ns, ");
+ sb.append("sigma=").append(mStats.getStandardDeviation()).append(", ");
+ sb.append("iteration=").append(mResults.size()).append(", ");
+ sb.append("values=").append(mResults.toString());
+ return sb.toString();
+ }
+
+ public void sendFullStatusReport(Instrumentation instrumentation, String key) {
+ if (mState != FINISHED) {
+ throw new IllegalStateException("The benchmark hasn't finished");
+ }
+ Log.i(TAG, key + summaryLine());
+ final Bundle status = new Bundle();
+ status.putLong(key + "_median", mStats.getMedian());
+ status.putLong(key + "_mean", (long) mStats.getMean());
+ status.putLong(key + "_stddev", (long) mStats.getStandardDeviation());
+ instrumentation.sendStatus(Activity.RESULT_OK, status);
+ }
+}
+
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/PerfManualStatusReporter.java b/apct-tests/perftests/utils/src/android/perftests/utils/PerfManualStatusReporter.java
new file mode 100644
index 0000000..0de6f1d
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/PerfManualStatusReporter.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.perftests.utils;
+
+import android.support.test.InstrumentationRegistry;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Use this rule to make sure we report the status after the test success.
+ *
+ * <code>
+ *
+ * @Rule public PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
+ * @Test public void functionName() {
+ * ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ *
+ * int[] src = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ * long elapsedTime = 0;
+ * while (state.keepRunning(elapsedTime)) {
+ * long startTime = System.nanoTime();
+ * int[] dest = new int[src.length];
+ * System.arraycopy(src, 0, dest, 0, src.length);
+ * elapsedTime = System.nanoTime() - startTime;
+ * }
+ * }
+ * </code>
+ *
+ * When test succeeded, the status report will use the key as
+ * "functionName_*"
+ */
+
+public class PerfManualStatusReporter implements TestRule {
+ private final ManualBenchmarkState mState;
+
+ public PerfManualStatusReporter() {
+ mState = new ManualBenchmarkState();
+ }
+
+ public ManualBenchmarkState getBenchmarkState() {
+ return mState;
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ base.evaluate();
+
+ mState.sendFullStatusReport(InstrumentationRegistry.getInstrumentation(),
+ description.getMethodName());
+ }
+ };
+ }
+}
+
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java b/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java
new file mode 100644
index 0000000..acc44a8
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.perftests.utils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class Stats {
+ private long mMedian, mMin, mMax;
+ private double mMean, mStandardDeviation;
+
+ /* Calculate stats in constructor. */
+ public Stats(List<Long> values) {
+ // make a copy since we're modifying it
+ values = new ArrayList<>(values);
+ final int size = values.size();
+ if (size < 2) {
+ throw new IllegalArgumentException("At least two results are necessary.");
+ }
+
+ Collections.sort(values);
+
+ mMedian = size % 2 == 0 ? (values.get(size / 2) + values.get(size / 2 - 1)) / 2 :
+ values.get(size / 2);
+
+ mMin = values.get(0);
+ mMax = values.get(values.size() - 1);
+
+ for (int i = 0; i < size; ++i) {
+ long result = values.get(i);
+ mMean += result;
+ }
+ mMean /= (double) size;
+
+ for (int i = 0; i < size; ++i) {
+ final double tmp = values.get(i) - mMean;
+ mStandardDeviation += tmp * tmp;
+ }
+ mStandardDeviation = Math.sqrt(mStandardDeviation / (double) (size - 1));
+ }
+
+ public double getMean() {
+ return mMean;
+ }
+
+ public long getMedian() {
+ return mMedian;
+ }
+
+ public long getMax() {
+ return mMax;
+ }
+
+ public long getMin() {
+ return mMin;
+ }
+
+ public double getStandardDeviation() {
+ return mStandardDeviation;
+ }
+}
diff --git a/api/current.txt b/api/current.txt
index b70d54e..1fb64ed 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -212,6 +212,7 @@
field public static final int accessibilityFeedbackType = 16843650; // 0x1010382
field public static final int accessibilityFlags = 16843652; // 0x1010384
field public static final int accessibilityLiveRegion = 16843758; // 0x10103ee
+ field public static final int accessibilityPaneTitle = 16844156; // 0x101057c
field public static final int accessibilityTraversalAfter = 16843986; // 0x10104d2
field public static final int accessibilityTraversalBefore = 16843985; // 0x10104d1
field public static final int accountPreferences = 16843423; // 0x101029f
@@ -601,6 +602,7 @@
field public static final int fingerprintAuthDrawable = 16844008; // 0x10104e8
field public static final int finishOnCloseSystemDialogs = 16843431; // 0x10102a7
field public static final int finishOnTaskLaunch = 16842772; // 0x1010014
+ field public static final int firstBaselineToTopHeight = 16844157; // 0x101057d
field public static final int firstDayOfWeek = 16843581; // 0x101033d
field public static final int fitsSystemWindows = 16842973; // 0x10100dd
field public static final int flipInterval = 16843129; // 0x1010179
@@ -797,6 +799,7 @@
field public static final int largeHeap = 16843610; // 0x101035a
field public static final int largeScreens = 16843398; // 0x1010286
field public static final int largestWidthLimitDp = 16843622; // 0x1010366
+ field public static final int lastBaselineToBottomHeight = 16844158; // 0x101057e
field public static final int launchMode = 16842781; // 0x101001d
field public static final int launchTaskBehindSourceAnimation = 16843922; // 0x1010492
field public static final int launchTaskBehindTargetAnimation = 16843921; // 0x1010491
@@ -854,6 +857,7 @@
field public static final int left = 16843181; // 0x10101ad
field public static final int letterSpacing = 16843958; // 0x10104b6
field public static final int level = 16844032; // 0x1010500
+ field public static final int lineHeight = 16844159; // 0x101057f
field public static final int lineSpacingExtra = 16843287; // 0x1010217
field public static final int lineSpacingMultiplier = 16843288; // 0x1010218
field public static final int lines = 16843092; // 0x1010154
@@ -5206,15 +5210,17 @@
field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
field public static final java.lang.String EXTRA_MEDIA_SESSION = "android.mediaSession";
field public static final java.lang.String EXTRA_MESSAGES = "android.messages";
+ field public static final java.lang.String EXTRA_MESSAGING_PERSON = "android.messagingUser";
field public static final java.lang.String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
field public static final java.lang.String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
- field public static final java.lang.String EXTRA_PEOPLE = "android.people";
+ field public static final deprecated java.lang.String EXTRA_PEOPLE = "android.people";
+ field public static final java.lang.String EXTRA_PEOPLE_LIST = "android.people.list";
field public static final java.lang.String EXTRA_PICTURE = "android.picture";
field public static final java.lang.String EXTRA_PROGRESS = "android.progress";
field public static final java.lang.String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
field public static final java.lang.String EXTRA_PROGRESS_MAX = "android.progressMax";
field public static final java.lang.String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
- field public static final java.lang.String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
+ field public static final deprecated java.lang.String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
field public static final java.lang.String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
field public static final java.lang.String EXTRA_SHOW_WHEN = "android.showWhen";
field public static final deprecated java.lang.String EXTRA_SMALL_ICON = "android.icon";
@@ -5288,8 +5294,19 @@
method public android.os.Bundle getExtras();
method public android.graphics.drawable.Icon getIcon();
method public android.app.RemoteInput[] getRemoteInputs();
+ method public int getSemanticAction();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.Notification.Action> CREATOR;
+ field public static final int SEMANTIC_ACTION_ARCHIVE = 5; // 0x5
+ field public static final int SEMANTIC_ACTION_DELETE = 4; // 0x4
+ field public static final int SEMANTIC_ACTION_MARK_AS_READ = 2; // 0x2
+ field public static final int SEMANTIC_ACTION_MARK_AS_UNREAD = 3; // 0x3
+ field public static final int SEMANTIC_ACTION_MUTE = 6; // 0x6
+ field public static final int SEMANTIC_ACTION_NONE = 0; // 0x0
+ field public static final int SEMANTIC_ACTION_REPLY = 1; // 0x1
+ field public static final int SEMANTIC_ACTION_THUMBS_DOWN = 9; // 0x9
+ field public static final int SEMANTIC_ACTION_THUMBS_UP = 8; // 0x8
+ field public static final int SEMANTIC_ACTION_UNMUTE = 7; // 0x7
field public android.app.PendingIntent actionIntent;
field public deprecated int icon;
field public java.lang.CharSequence title;
@@ -5305,6 +5322,7 @@
method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Extender);
method public android.os.Bundle getExtras();
method public android.app.Notification.Action.Builder setAllowGeneratedReplies(boolean);
+ method public android.app.Notification.Action.Builder setSemanticAction(int);
}
public static abstract interface Notification.Action.Extender {
@@ -5354,7 +5372,8 @@
method public deprecated android.app.Notification.Builder addAction(int, java.lang.CharSequence, android.app.PendingIntent);
method public android.app.Notification.Builder addAction(android.app.Notification.Action);
method public android.app.Notification.Builder addExtras(android.os.Bundle);
- method public android.app.Notification.Builder addPerson(java.lang.String);
+ method public deprecated android.app.Notification.Builder addPerson(java.lang.String);
+ method public android.app.Notification.Builder addPerson(android.app.Notification.Person);
method public android.app.Notification build();
method public android.widget.RemoteViews createBigContentView();
method public android.widget.RemoteViews createContentView();
@@ -5477,14 +5496,17 @@
}
public static class Notification.MessagingStyle extends android.app.Notification.Style {
- ctor public Notification.MessagingStyle(java.lang.CharSequence);
+ ctor public deprecated Notification.MessagingStyle(java.lang.CharSequence);
+ ctor public Notification.MessagingStyle(android.app.Notification.Person);
method public android.app.Notification.MessagingStyle addHistoricMessage(android.app.Notification.MessagingStyle.Message);
- method public android.app.Notification.MessagingStyle addMessage(java.lang.CharSequence, long, java.lang.CharSequence);
+ method public deprecated android.app.Notification.MessagingStyle addMessage(java.lang.CharSequence, long, java.lang.CharSequence);
+ method public android.app.Notification.MessagingStyle addMessage(java.lang.CharSequence, long, android.app.Notification.Person);
method public android.app.Notification.MessagingStyle addMessage(android.app.Notification.MessagingStyle.Message);
method public java.lang.CharSequence getConversationTitle();
method public java.util.List<android.app.Notification.MessagingStyle.Message> getHistoricMessages();
method public java.util.List<android.app.Notification.MessagingStyle.Message> getMessages();
- method public java.lang.CharSequence getUserDisplayName();
+ method public android.app.Notification.Person getUser();
+ method public deprecated java.lang.CharSequence getUserDisplayName();
method public boolean isGroupConversation();
method public android.app.Notification.MessagingStyle setConversationTitle(java.lang.CharSequence);
method public android.app.Notification.MessagingStyle setGroupConversation(boolean);
@@ -5492,16 +5514,34 @@
}
public static final class Notification.MessagingStyle.Message {
- ctor public Notification.MessagingStyle.Message(java.lang.CharSequence, long, java.lang.CharSequence);
+ ctor public deprecated Notification.MessagingStyle.Message(java.lang.CharSequence, long, java.lang.CharSequence);
+ ctor public Notification.MessagingStyle.Message(java.lang.CharSequence, long, android.app.Notification.Person);
method public java.lang.String getDataMimeType();
method public android.net.Uri getDataUri();
method public android.os.Bundle getExtras();
- method public java.lang.CharSequence getSender();
+ method public deprecated java.lang.CharSequence getSender();
+ method public android.app.Notification.Person getSenderPerson();
method public java.lang.CharSequence getText();
method public long getTimestamp();
method public android.app.Notification.MessagingStyle.Message setData(java.lang.String, android.net.Uri);
}
+ public static final class Notification.Person implements android.os.Parcelable {
+ ctor protected Notification.Person(android.os.Parcel);
+ ctor public Notification.Person();
+ method public int describeContents();
+ method public android.graphics.drawable.Icon getIcon();
+ method public java.lang.String getKey();
+ method public java.lang.CharSequence getName();
+ method public java.lang.String getUri();
+ method public android.app.Notification.Person setIcon(android.graphics.drawable.Icon);
+ method public android.app.Notification.Person setKey(java.lang.String);
+ method public android.app.Notification.Person setName(java.lang.CharSequence);
+ method public android.app.Notification.Person setUri(java.lang.String);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.Notification.Person> CREATOR;
+ }
+
public static abstract class Notification.Style {
ctor public Notification.Style();
method public android.app.Notification build();
@@ -6298,6 +6338,7 @@
method public void onReceive(android.content.Context, android.content.Intent);
method public void onSecurityLogsAvailable(android.content.Context, android.content.Intent);
method public void onSystemUpdatePending(android.content.Context, android.content.Intent, long);
+ method public void onTransferOwnershipComplete(android.content.Context, android.os.PersistableBundle);
method public void onUserAdded(android.content.Context, android.content.Intent, android.os.UserHandle);
method public void onUserRemoved(android.content.Context, android.content.Intent, android.os.UserHandle);
field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED";
@@ -6315,6 +6356,8 @@
field public static final java.lang.String DEVICE_ADMIN_META_DATA = "android.app.device_admin";
field public static final java.lang.String EXTRA_DISABLE_WARNING = "android.app.extra.DISABLE_WARNING";
field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE";
+ field public static final java.lang.String EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE = "android.app.extra.TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE";
+ field public static final java.lang.String SUPPORT_TRANSFER_OWNERSHIP_META_DATA = "android.app.support_transfer_ownership";
}
public class DeviceAdminService extends android.app.Service {
@@ -6328,7 +6371,7 @@
method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName);
method public void addUserRestriction(android.content.ComponentName, java.lang.String);
method public boolean bindDeviceAdminServiceAsUser(android.content.ComponentName, android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
- method public boolean clearApplicationUserData(android.content.ComponentName, java.lang.String, android.app.admin.DevicePolicyManager.OnClearApplicationUserDataListener, java.util.concurrent.Executor);
+ method public boolean clearApplicationUserData(android.content.ComponentName, java.lang.String, java.util.concurrent.Executor, android.app.admin.DevicePolicyManager.OnClearApplicationUserDataListener);
method public void clearCrossProfileIntentFilters(android.content.ComponentName);
method public deprecated void clearDeviceOwnerApp(java.lang.String);
method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
@@ -6358,12 +6401,14 @@
method public java.util.List<java.lang.String> getDelegatePackages(android.content.ComponentName, java.lang.String);
method public java.util.List<java.lang.String> getDelegatedScopes(android.content.ComponentName, java.lang.String);
method public java.lang.CharSequence getDeviceOwnerLockScreenInfo();
+ method public java.lang.CharSequence getEndUserSessionMessage(android.content.ComponentName);
method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
method public java.util.List<java.lang.String> getKeepUninstalledPackages(android.content.ComponentName);
method public int getKeyguardDisabledFeatures(android.content.ComponentName);
method public int getLockTaskFeatures(android.content.ComponentName);
method public java.lang.String[] getLockTaskPackages(android.content.ComponentName);
method public java.lang.CharSequence getLongSupportMessage(android.content.ComponentName);
+ method public android.content.ComponentName getMandatoryBackupTransport();
method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
method public long getMaximumTimeToLock(android.content.ComponentName);
method public int getOrganizationColor(android.content.ComponentName);
@@ -6392,6 +6437,7 @@
method public boolean getScreenCaptureDisabled(android.content.ComponentName);
method public java.util.List<android.os.UserHandle> getSecondaryUsers(android.content.ComponentName);
method public java.lang.CharSequence getShortSupportMessage(android.content.ComponentName);
+ method public java.lang.CharSequence getStartUserSessionMessage(android.content.ComponentName);
method public boolean getStorageEncryption(android.content.ComponentName);
method public int getStorageEncryptionStatus();
method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
@@ -6454,6 +6500,7 @@
method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean);
method public void setDelegatedScopes(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>);
method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence);
+ method public void setEndUserSessionMessage(android.content.ComponentName, java.lang.CharSequence);
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public void setKeepUninstalledPackages(android.content.ComponentName, java.util.List<java.lang.String>);
method public boolean setKeyPairCertificate(android.content.ComponentName, java.lang.String, java.util.List<java.security.cert.Certificate>, boolean);
@@ -6463,6 +6510,7 @@
method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException;
method public void setLogoutEnabled(android.content.ComponentName, boolean);
method public void setLongSupportMessage(android.content.ComponentName, java.lang.CharSequence);
+ method public void setMandatoryBackupTransport(android.content.ComponentName, android.content.ComponentName);
method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
method public void setMaximumTimeToLock(android.content.ComponentName, long);
@@ -6496,6 +6544,7 @@
method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public void setSecurityLoggingEnabled(android.content.ComponentName, boolean);
method public void setShortSupportMessage(android.content.ComponentName, java.lang.CharSequence);
+ method public void setStartUserSessionMessage(android.content.ComponentName, java.lang.CharSequence);
method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
method public int setStorageEncryption(android.content.ComponentName, boolean);
method public void setSystemSetting(android.content.ComponentName, java.lang.String, java.lang.String);
@@ -6505,8 +6554,10 @@
method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
method public void setUserIcon(android.content.ComponentName, android.graphics.Bitmap);
+ method public boolean startUserInBackground(android.content.ComponentName, android.os.UserHandle);
method public boolean stopUser(android.content.ComponentName, android.os.UserHandle);
method public boolean switchUser(android.content.ComponentName, android.os.UserHandle);
+ method public void transferOwnership(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
method public void uninstallAllUserCaCerts(android.content.ComponentName);
method public void uninstallCaCert(android.content.ComponentName, byte[]);
method public void wipeData(int);
@@ -6516,6 +6567,7 @@
field public static final java.lang.String ACTION_DEVICE_ADMIN_SERVICE = "android.app.action.DEVICE_ADMIN_SERVICE";
field public static final java.lang.String ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED";
field public static final java.lang.String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED";
+ field public static final java.lang.String ACTION_PROFILE_OWNER_CHANGED = "android.app.action.PROFILE_OWNER_CHANGED";
field public static final java.lang.String ACTION_PROVISIONING_SUCCESSFUL = "android.app.action.PROVISIONING_SUCCESSFUL";
field public static final java.lang.String ACTION_PROVISION_MANAGED_DEVICE = "android.app.action.PROVISION_MANAGED_DEVICE";
field public static final java.lang.String ACTION_PROVISION_MANAGED_PROFILE = "android.app.action.PROVISION_MANAGED_PROFILE";
@@ -6615,7 +6667,6 @@
field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
- field public static final int START_USER_IN_BACKGROUND = 8; // 0x8
field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
}
@@ -7025,8 +7076,8 @@
public final class Slice implements android.os.Parcelable {
ctor protected Slice(android.os.Parcel);
- method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
- method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
+ method public static deprecated android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public static deprecated android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
method public int describeContents();
method public java.util.List<java.lang.String> getHints();
method public java.util.List<android.app.slice.SliceItem> getItems();
@@ -7113,7 +7164,10 @@
}
public class SliceManager {
+ method public android.app.slice.Slice bindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public android.app.slice.Slice bindSlice(android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
method public java.util.List<android.app.slice.SliceSpec> getPinnedSpecs(android.net.Uri);
+ method public java.util.Collection<android.net.Uri> getSliceDescendants(android.net.Uri);
method public void pinSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>);
method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>, android.os.Handler);
@@ -7132,7 +7186,7 @@
method public final java.lang.String getType(android.net.Uri);
method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
- method public deprecated android.app.slice.Slice onBindSlice(android.net.Uri);
+ method public java.util.Collection<android.net.Uri> onGetSliceDescendants(android.net.Uri);
method public android.net.Uri onMapIntentToUri(android.content.Intent);
method public void onSlicePinned(android.net.Uri);
method public void onSliceUnpinned(android.net.Uri);
@@ -11114,7 +11168,7 @@
field public static final java.lang.String FEATURE_USB_HOST = "android.hardware.usb.host";
field public static final java.lang.String FEATURE_VERIFIED_BOOT = "android.software.verified_boot";
field public static final java.lang.String FEATURE_VR_HEADTRACKING = "android.hardware.vr.headtracking";
- field public static final java.lang.String FEATURE_VR_MODE = "android.software.vr.mode";
+ field public static final deprecated java.lang.String FEATURE_VR_MODE = "android.software.vr.mode";
field public static final java.lang.String FEATURE_VR_MODE_HIGH_PERFORMANCE = "android.hardware.vr.high_performance";
field public static final java.lang.String FEATURE_VULKAN_HARDWARE_COMPUTE = "android.hardware.vulkan.compute";
field public static final java.lang.String FEATURE_VULKAN_HARDWARE_LEVEL = "android.hardware.vulkan.level";
@@ -20842,7 +20896,6 @@
method public int getMaxWidth();
method public java.lang.CharSequence getTextForImeAction(int);
method public android.app.Dialog getWindow();
- method public void hideSoftInputFromInputMethod(int);
method public void hideStatusIcon();
method public void hideWindow();
method public boolean isExtractViewShown();
@@ -20890,6 +20943,7 @@
method public void onWindowHidden();
method public void onWindowShown();
method public void requestHideSelf(int);
+ method public void requestShowSelf(int);
method public boolean sendDefaultEditorAction(boolean);
method public void sendDownUpKeyEvents(int);
method public void sendKeyChar(char);
@@ -20902,7 +20956,6 @@
method public void setInputMethodAndSubtype(java.lang.String, android.view.inputmethod.InputMethodSubtype);
method public void setInputView(android.view.View);
method public boolean shouldOfferSwitchingToNextInputMethod();
- method public void showSoftInputFromInputMethod(int);
method public void showStatusIcon(int);
method public void showWindow(boolean);
method public void switchInputMethod(java.lang.String);
@@ -23183,6 +23236,7 @@
field public static final java.lang.String KEY_MAX_WIDTH = "max-width";
field public static final java.lang.String KEY_MIME = "mime";
field public static final java.lang.String KEY_OPERATING_RATE = "operating-rate";
+ field public static final java.lang.String KEY_OUTPUT_REORDER_DEPTH = "output-reorder-depth";
field public static final java.lang.String KEY_PCM_ENCODING = "pcm-encoding";
field public static final java.lang.String KEY_PRIORITY = "priority";
field public static final java.lang.String KEY_PROFILE = "profile";
@@ -26177,12 +26231,18 @@
}
public final class IpSecManager {
- method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
- method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
- method public void applyTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
+ method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
+ method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+ method public void applyTransportModeTransform(java.net.Socket, int, android.net.IpSecTransform) throws java.io.IOException;
+ method public void applyTransportModeTransform(java.net.DatagramSocket, int, android.net.IpSecTransform) throws java.io.IOException;
+ method public void applyTransportModeTransform(java.io.FileDescriptor, int, android.net.IpSecTransform) throws java.io.IOException;
method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
- method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
+ method public void removeTransportModeTransforms(java.net.Socket) throws java.io.IOException;
+ method public void removeTransportModeTransforms(java.net.DatagramSocket) throws java.io.IOException;
+ method public void removeTransportModeTransforms(java.io.FileDescriptor) throws java.io.IOException;
+ field public static final int DIRECTION_IN = 0; // 0x0
+ field public static final int DIRECTION_OUT = 1; // 0x1
}
public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
@@ -26205,18 +26265,15 @@
public final class IpSecTransform implements java.lang.AutoCloseable {
method public void close();
- field public static final int DIRECTION_IN = 0; // 0x0
- field public static final int DIRECTION_OUT = 1; // 0x1
}
public static class IpSecTransform.Builder {
ctor public IpSecTransform.Builder(android.content.Context);
- method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
- method public android.net.IpSecTransform.Builder setAuthenticatedEncryption(int, android.net.IpSecAlgorithm);
- method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm);
- method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm);
+ method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+ method public android.net.IpSecTransform.Builder setAuthenticatedEncryption(android.net.IpSecAlgorithm);
+ method public android.net.IpSecTransform.Builder setAuthentication(android.net.IpSecAlgorithm);
+ method public android.net.IpSecTransform.Builder setEncryption(android.net.IpSecAlgorithm);
method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int);
- method public android.net.IpSecTransform.Builder setSpi(int, android.net.IpSecManager.SecurityParameterIndex);
}
public class LinkAddress implements android.os.Parcelable {
@@ -26236,7 +26293,9 @@
method public android.net.ProxyInfo getHttpProxy();
method public java.lang.String getInterfaceName();
method public java.util.List<android.net.LinkAddress> getLinkAddresses();
+ method public java.lang.String getPrivateDnsServerName();
method public java.util.List<android.net.RouteInfo> getRoutes();
+ method public boolean isPrivateDnsActive();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.LinkProperties> CREATOR;
}
@@ -26299,10 +26358,10 @@
}
public final class MacAddress implements android.os.Parcelable {
- method public int addressType();
method public int describeContents();
method public static android.net.MacAddress fromBytes(byte[]);
method public static android.net.MacAddress fromString(java.lang.String);
+ method public int getAddressType();
method public boolean isLocallyAssigned();
method public byte[] toByteArray();
method public java.lang.String toOuiString();
@@ -26312,7 +26371,6 @@
field public static final int TYPE_BROADCAST = 3; // 0x3
field public static final int TYPE_MULTICAST = 2; // 0x2
field public static final int TYPE_UNICAST = 1; // 0x1
- field public static final int TYPE_UNKNOWN = 0; // 0x0
}
public class MailTo {
@@ -32092,6 +32150,7 @@
}
public final class PowerManager {
+ method public int getLocationPowerSaveMode();
method public boolean isDeviceIdleMode();
method public boolean isIgnoringBatteryOptimizations(java.lang.String);
method public boolean isInteractive();
@@ -32105,6 +32164,10 @@
field public static final java.lang.String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
field public static final java.lang.String ACTION_POWER_SAVE_MODE_CHANGED = "android.os.action.POWER_SAVE_MODE_CHANGED";
field public static final deprecated int FULL_WAKE_LOCK = 26; // 0x1a
+ field public static final int LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF = 2; // 0x2
+ field public static final int LOCATION_MODE_FOREGROUND_ONLY = 3; // 0x3
+ field public static final int LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF = 1; // 0x1
+ field public static final int LOCATION_MODE_NO_CHANGE = 0; // 0x0
field public static final int ON_AFTER_RELEASE = 536870912; // 0x20000000
field public static final int PARTIAL_WAKE_LOCK = 1; // 0x1
field public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = 32; // 0x20
@@ -32160,6 +32223,7 @@
field public static final int THREAD_PRIORITY_MORE_FAVORABLE = -1; // 0xffffffff
field public static final int THREAD_PRIORITY_URGENT_AUDIO = -19; // 0xffffffed
field public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8; // 0xfffffff8
+ field public static final int THREAD_PRIORITY_VIDEO = -10; // 0xfffffff6
}
public abstract class ProxyFileDescriptorCallback {
@@ -32392,28 +32456,31 @@
method public boolean isUserRunningOrStopping(android.os.UserHandle);
method public boolean isUserUnlocked();
method public boolean isUserUnlocked(android.os.UserHandle);
+ method public boolean requestQuietModeEnabled(boolean, android.os.UserHandle);
method public deprecated boolean setRestrictionsChallenge(java.lang.String);
method public deprecated void setUserRestriction(java.lang.String, boolean);
method public deprecated void setUserRestrictions(android.os.Bundle);
method public deprecated void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
method public static boolean supportsMultipleUsers();
- method public boolean trySetQuietModeEnabled(boolean, android.os.UserHandle);
field public static final java.lang.String ALLOW_PARENT_PROFILE_APP_LINKING = "allow_parent_profile_app_linking";
field public static final java.lang.String DISALLOW_ADD_MANAGED_PROFILE = "no_add_managed_profile";
field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user";
field public static final java.lang.String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
field public static final java.lang.String DISALLOW_AIRPLANE_MODE = "no_airplane_mode";
+ field public static final java.lang.String DISALLOW_AMBIENT_DISPLAY = "no_ambient_display";
field public static final java.lang.String DISALLOW_APPS_CONTROL = "no_control_apps";
field public static final java.lang.String DISALLOW_AUTOFILL = "no_autofill";
field public static final java.lang.String DISALLOW_BLUETOOTH = "no_bluetooth";
field public static final java.lang.String DISALLOW_BLUETOOTH_SHARING = "no_bluetooth_sharing";
field public static final java.lang.String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth";
+ field public static final java.lang.String DISALLOW_CONFIG_BRIGHTNESS = "no_config_brightness";
field public static final java.lang.String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts";
field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
field public static final java.lang.String DISALLOW_CONFIG_DATE_TIME = "no_config_date_time";
field public static final java.lang.String DISALLOW_CONFIG_LOCALE = "no_config_locale";
field public static final java.lang.String DISALLOW_CONFIG_LOCATION_MODE = "no_config_location_mode";
field public static final java.lang.String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
+ field public static final java.lang.String DISALLOW_CONFIG_SCREEN_TIMEOUT = "no_config_screen_timeout";
field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn";
field public static final java.lang.String DISALLOW_CONFIG_WIFI = "no_config_wifi";
@@ -32435,6 +32502,7 @@
field public static final java.lang.String DISALLOW_SAFE_BOOT = "no_safe_boot";
field public static final java.lang.String DISALLOW_SET_USER_ICON = "no_set_user_icon";
field public static final java.lang.String DISALLOW_SET_WALLPAPER = "no_set_wallpaper";
+ field public static final java.lang.String DISALLOW_SHARE_INTO_MANAGED_PROFILE = "no_sharing_into_profile";
field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location";
field public static final java.lang.String DISALLOW_SMS = "no_sms";
field public static final java.lang.String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs";
@@ -36380,6 +36448,7 @@
field public static final java.lang.String ITEM_TYPE = "vnd.android.cursor.item/voicemail";
field public static final java.lang.String LAST_MODIFIED = "last_modified";
field public static final java.lang.String MIME_TYPE = "mime_type";
+ field public static final java.lang.String NEW = "new";
field public static final java.lang.String NUMBER = "number";
field public static final java.lang.String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
field public static final java.lang.String PHONE_ACCOUNT_ID = "subscription_id";
@@ -41097,6 +41166,7 @@
method public void onServiceStateChanged(android.telephony.ServiceState);
method public deprecated void onSignalStrengthChanged(int);
method public void onSignalStrengthsChanged(android.telephony.SignalStrength);
+ method public void onUserMobileDataStateChanged(boolean);
field public static final int LISTEN_CALL_FORWARDING_INDICATOR = 8; // 0x8
field public static final int LISTEN_CALL_STATE = 32; // 0x20
field public static final int LISTEN_CELL_INFO = 1024; // 0x400
@@ -41108,6 +41178,7 @@
field public static final int LISTEN_SERVICE_STATE = 1; // 0x1
field public static final deprecated int LISTEN_SIGNAL_STRENGTH = 2; // 0x2
field public static final int LISTEN_SIGNAL_STRENGTHS = 256; // 0x100
+ field public static final int LISTEN_USER_MOBILE_DATA_STATE = 524288; // 0x80000
}
public final class RadioAccessSpecifier implements android.os.Parcelable {
@@ -41588,6 +41659,78 @@
}
+package android.telephony.data {
+
+ public class ApnSetting implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.lang.String getApnName();
+ method public int getAuthType();
+ method public java.lang.String getEntryName();
+ method public int getId();
+ method public int getMmsPort();
+ method public java.net.InetAddress getMmsProxy();
+ method public java.net.URL getMmsc();
+ method public java.lang.String getMvnoType();
+ method public java.lang.String getOperatorNumeric();
+ method public java.lang.String getPassword();
+ method public int getPort();
+ method public java.lang.String getProtocol();
+ method public java.net.InetAddress getProxy();
+ method public java.lang.String getRoamingProtocol();
+ method public java.util.List<java.lang.String> getTypes();
+ method public java.lang.String getUser();
+ method public boolean isEnabled();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int AUTH_TYPE_CHAP = 2; // 0x2
+ field public static final int AUTH_TYPE_NONE = 0; // 0x0
+ field public static final int AUTH_TYPE_PAP = 1; // 0x1
+ field public static final int AUTH_TYPE_PAP_OR_CHAP = 3; // 0x3
+ field public static final android.os.Parcelable.Creator<android.telephony.data.ApnSetting> CREATOR;
+ field public static final java.lang.String MVNO_TYPE_GID = "gid";
+ field public static final java.lang.String MVNO_TYPE_ICCID = "iccid";
+ field public static final java.lang.String MVNO_TYPE_IMSI = "imsi";
+ field public static final java.lang.String MVNO_TYPE_SPN = "spn";
+ field public static final java.lang.String PROTOCOL_IP = "IP";
+ field public static final java.lang.String PROTOCOL_IPV4V6 = "IPV4V6";
+ field public static final java.lang.String PROTOCOL_IPV6 = "IPV6";
+ field public static final java.lang.String PROTOCOL_PPP = "PPP";
+ field public static final java.lang.String TYPE_ALL = "*";
+ field public static final java.lang.String TYPE_CBS = "cbs";
+ field public static final java.lang.String TYPE_DEFAULT = "default";
+ field public static final java.lang.String TYPE_DUN = "dun";
+ field public static final java.lang.String TYPE_EMERGENCY = "emergency";
+ field public static final java.lang.String TYPE_FOTA = "fota";
+ field public static final java.lang.String TYPE_HIPRI = "hipri";
+ field public static final java.lang.String TYPE_IA = "ia";
+ field public static final java.lang.String TYPE_IMS = "ims";
+ field public static final java.lang.String TYPE_MMS = "mms";
+ field public static final java.lang.String TYPE_SUPL = "supl";
+ }
+
+ public static class ApnSetting.Builder {
+ ctor public ApnSetting.Builder();
+ method public android.telephony.data.ApnSetting build();
+ method public android.telephony.data.ApnSetting.Builder setApnName(java.lang.String);
+ method public android.telephony.data.ApnSetting.Builder setAuthType(int);
+ method public android.telephony.data.ApnSetting.Builder setCarrierEnabled(boolean);
+ method public android.telephony.data.ApnSetting.Builder setEntryName(java.lang.String);
+ method public android.telephony.data.ApnSetting.Builder setId(int);
+ method public android.telephony.data.ApnSetting.Builder setMmsPort(int);
+ method public android.telephony.data.ApnSetting.Builder setMmsProxy(java.net.InetAddress);
+ method public android.telephony.data.ApnSetting.Builder setMmsc(java.net.URL);
+ method public android.telephony.data.ApnSetting.Builder setMvnoType(java.lang.String);
+ method public android.telephony.data.ApnSetting.Builder setOperatorNumeric(java.lang.String);
+ method public android.telephony.data.ApnSetting.Builder setPassword(java.lang.String);
+ method public android.telephony.data.ApnSetting.Builder setPort(int);
+ method public android.telephony.data.ApnSetting.Builder setProtocol(java.lang.String);
+ method public android.telephony.data.ApnSetting.Builder setProxy(java.net.InetAddress);
+ method public android.telephony.data.ApnSetting.Builder setRoamingProtocol(java.lang.String);
+ method public android.telephony.data.ApnSetting.Builder setTypes(java.util.List<java.lang.String>);
+ method public android.telephony.data.ApnSetting.Builder setUser(java.lang.String);
+ }
+
+}
+
package android.telephony.gsm {
public class GsmCellLocation extends android.telephony.CellLocation {
@@ -42172,20 +42315,9 @@
method public boolean isAllowed(char);
}
- public abstract interface NoCopySpan {
- }
-
- public static class NoCopySpan.Concrete implements android.text.NoCopySpan {
- ctor public NoCopySpan.Concrete();
- }
-
- public abstract interface ParcelableSpan implements android.os.Parcelable {
- method public abstract int getSpanTypeId();
- }
-
- public class PremeasuredText implements android.text.Spanned {
- method public static android.text.PremeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic);
- method public static android.text.PremeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic, int, int);
+ public class MeasuredText implements android.text.Spanned {
+ method public static android.text.MeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic);
+ method public static android.text.MeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic, int, int);
method public char charAt(int);
method public int getEnd();
method public android.text.TextPaint getPaint();
@@ -42204,6 +42336,17 @@
method public java.lang.CharSequence subSequence(int, int);
}
+ public abstract interface NoCopySpan {
+ }
+
+ public static class NoCopySpan.Concrete implements android.text.NoCopySpan {
+ ctor public NoCopySpan.Concrete();
+ }
+
+ public abstract interface ParcelableSpan implements android.os.Parcelable {
+ method public abstract int getSpanTypeId();
+ }
+
public class Selection {
method public static boolean extendDown(android.text.Spannable, android.text.Layout);
method public static boolean extendLeft(android.text.Spannable, android.text.Layout);
@@ -45144,6 +45287,7 @@
field public static final int KEYCODE_8 = 15; // 0xf
field public static final int KEYCODE_9 = 16; // 0x10
field public static final int KEYCODE_A = 29; // 0x1d
+ field public static final int KEYCODE_ALL_APPS = 284; // 0x11c
field public static final int KEYCODE_ALT_LEFT = 57; // 0x39
field public static final int KEYCODE_ALT_RIGHT = 58; // 0x3a
field public static final int KEYCODE_APOSTROPHE = 75; // 0x4b
@@ -46189,6 +46333,7 @@
method public java.lang.CharSequence getAccessibilityClassName();
method public int getAccessibilityLiveRegion();
method public android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider();
+ method public java.lang.CharSequence getAccessibilityPaneTitle();
method public int getAccessibilityTraversalAfter();
method public int getAccessibilityTraversalBefore();
method public float getAlpha();
@@ -46512,6 +46657,7 @@
method public void sendAccessibilityEventUnchecked(android.view.accessibility.AccessibilityEvent);
method public void setAccessibilityDelegate(android.view.View.AccessibilityDelegate);
method public void setAccessibilityLiveRegion(int);
+ method public void setAccessibilityPaneTitle(java.lang.CharSequence);
method public void setAccessibilityTraversalAfter(int);
method public void setAccessibilityTraversalBefore(int);
method public void setActivated(boolean);
@@ -47762,7 +47908,6 @@
field public static final int FIRST_APPLICATION_WINDOW = 1; // 0x1
field public static final int FIRST_SUB_WINDOW = 1000; // 0x3e8
field public static final int FIRST_SYSTEM_WINDOW = 2000; // 0x7d0
- field public static final long FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA = 1L; // 0x1L
field public static final int FLAGS_CHANGED = 4; // 0x4
field public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 1; // 0x1
field public static final int FLAG_ALT_FOCUSABLE_IM = 131072; // 0x20000
@@ -47800,6 +47945,9 @@
field public static final int LAST_SUB_WINDOW = 1999; // 0x7cf
field public static final int LAST_SYSTEM_WINDOW = 2999; // 0xbb7
field public static final int LAYOUT_CHANGED = 1; // 0x1
+ field public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS = 1; // 0x1
+ field public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT = 0; // 0x0
+ field public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER = 2; // 0x2
field public static final int MEMORY_TYPE_CHANGED = 256; // 0x100
field public static final deprecated int MEMORY_TYPE_GPU = 2; // 0x2
field public static final deprecated int MEMORY_TYPE_HARDWARE = 1; // 0x1
@@ -47857,11 +48005,11 @@
field public float buttonBrightness;
field public float dimAmount;
field public int flags;
- field public long flags2;
field public int format;
field public int gravity;
field public float horizontalMargin;
field public float horizontalWeight;
+ field public int layoutInDisplayCutoutMode;
field public deprecated int memoryType;
field public java.lang.String packageName;
field public int preferredDisplayModeId;
@@ -47909,6 +48057,9 @@
method public void setPackageName(java.lang.CharSequence);
method public void writeToParcel(android.os.Parcel, int);
field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
+ field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10
+ field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20
+ field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 1; // 0x1
field public static final int CONTENT_CHANGE_TYPE_TEXT = 2; // 0x2
field public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0; // 0x0
@@ -48019,6 +48170,7 @@
method public int getMaxTextLength();
method public int getMovementGranularities();
method public java.lang.CharSequence getPackageName();
+ method public java.lang.CharSequence getPaneTitle();
method public android.view.accessibility.AccessibilityNodeInfo getParent();
method public android.view.accessibility.AccessibilityNodeInfo.RangeInfo getRangeInfo();
method public java.lang.CharSequence getText();
@@ -48096,6 +48248,7 @@
method public void setMovementGranularities(int);
method public void setMultiLine(boolean);
method public void setPackageName(java.lang.CharSequence);
+ method public void setPaneTitle(java.lang.CharSequence);
method public void setParent(android.view.View);
method public void setParent(android.view.View, int);
method public void setPassword(boolean);
@@ -49132,14 +49285,13 @@
method public android.graphics.drawable.Drawable getSecondaryIcon(int);
method public android.content.Intent getSecondaryIntent(int);
method public java.lang.CharSequence getSecondaryLabel(int);
- method public android.view.View.OnClickListener getSecondaryOnClickListener(int);
method public java.lang.String getSignature();
method public java.lang.String getText();
}
public static final class TextClassification.Builder {
ctor public TextClassification.Builder();
- method public android.view.textclassifier.TextClassification.Builder addSecondaryAction(android.content.Intent, java.lang.String, android.graphics.drawable.Drawable, android.view.View.OnClickListener);
+ method public android.view.textclassifier.TextClassification.Builder addSecondaryAction(android.content.Intent, java.lang.String, android.graphics.drawable.Drawable);
method public android.view.textclassifier.TextClassification build();
method public android.view.textclassifier.TextClassification.Builder clearSecondaryActions();
method public android.view.textclassifier.TextClassification.Builder setEntityType(java.lang.String, float);
@@ -49147,15 +49299,18 @@
method public android.view.textclassifier.TextClassification.Builder setIntent(android.content.Intent);
method public android.view.textclassifier.TextClassification.Builder setLabel(java.lang.String);
method public android.view.textclassifier.TextClassification.Builder setOnClickListener(android.view.View.OnClickListener);
- method public android.view.textclassifier.TextClassification.Builder setPrimaryAction(android.content.Intent, java.lang.String, android.graphics.drawable.Drawable, android.view.View.OnClickListener);
+ method public android.view.textclassifier.TextClassification.Builder setPrimaryAction(android.content.Intent, java.lang.String, android.graphics.drawable.Drawable);
method public android.view.textclassifier.TextClassification.Builder setSignature(java.lang.String);
method public android.view.textclassifier.TextClassification.Builder setText(java.lang.String);
}
- public static final class TextClassification.Options {
+ public static final class TextClassification.Options implements android.os.Parcelable {
ctor public TextClassification.Options();
+ method public int describeContents();
method public android.os.LocaleList getDefaultLocales();
method public android.view.textclassifier.TextClassification.Options setDefaultLocales(android.os.LocaleList);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassification.Options> CREATOR;
}
public final class TextClassificationManager {
@@ -49185,16 +49340,22 @@
field public static final java.lang.String TYPE_URL = "url";
}
- public static final class TextClassifier.EntityConfig {
+ public static final class TextClassifier.EntityConfig implements android.os.Parcelable {
ctor public TextClassifier.EntityConfig(int);
+ method public int describeContents();
method public android.view.textclassifier.TextClassifier.EntityConfig excludeEntities(java.lang.String...);
method public java.util.List<java.lang.String> getEntities(android.view.textclassifier.TextClassifier);
method public android.view.textclassifier.TextClassifier.EntityConfig includeEntities(java.lang.String...);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifier.EntityConfig> CREATOR;
}
- public final class TextLinks {
+ public final class TextLinks implements android.os.Parcelable {
method public boolean apply(android.text.SpannableString, java.util.function.Function<android.view.textclassifier.TextLinks.TextLink, android.text.style.ClickableSpan>);
+ method public int describeContents();
method public java.util.Collection<android.view.textclassifier.TextLinks.TextLink> getLinks();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextLinks> CREATOR;
}
public static final class TextLinks.Builder {
@@ -49203,21 +49364,27 @@
method public android.view.textclassifier.TextLinks build();
}
- public static final class TextLinks.Options {
+ public static final class TextLinks.Options implements android.os.Parcelable {
ctor public TextLinks.Options();
+ method public int describeContents();
method public android.os.LocaleList getDefaultLocales();
method public android.view.textclassifier.TextClassifier.EntityConfig getEntityConfig();
method public android.view.textclassifier.TextLinks.Options setDefaultLocales(android.os.LocaleList);
method public android.view.textclassifier.TextLinks.Options setEntityConfig(android.view.textclassifier.TextClassifier.EntityConfig);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextLinks.Options> CREATOR;
}
- public static final class TextLinks.TextLink {
+ public static final class TextLinks.TextLink implements android.os.Parcelable {
ctor public TextLinks.TextLink(java.lang.String, int, int, java.util.Map<java.lang.String, java.lang.Float>);
+ method public int describeContents();
method public float getConfidenceScore(java.lang.String);
method public int getEnd();
method public java.lang.String getEntity(int);
method public int getEntityCount();
method public int getStart();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextLinks.TextLink> CREATOR;
}
public final class TextSelection {
@@ -49236,10 +49403,13 @@
method public android.view.textclassifier.TextSelection.Builder setSignature(java.lang.String);
}
- public static final class TextSelection.Options {
+ public static final class TextSelection.Options implements android.os.Parcelable {
ctor public TextSelection.Options();
+ method public int describeContents();
method public android.os.LocaleList getDefaultLocales();
method public android.view.textclassifier.TextSelection.Options setDefaultLocales(android.os.LocaleList);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextSelection.Options> CREATOR;
}
}
@@ -52357,6 +52527,7 @@
method public int getExtendedPaddingBottom();
method public int getExtendedPaddingTop();
method public android.text.InputFilter[] getFilters();
+ method public int getFirstBaselineToTopHeight();
method public java.lang.String getFontFeatureSettings();
method public java.lang.String getFontVariationSettings();
method public boolean getFreezesText();
@@ -52374,6 +52545,7 @@
method public int getInputType();
method public int getJustificationMode();
method public final android.text.method.KeyListener getKeyListener();
+ method public int getLastBaselineToBottomHeight();
method public final android.text.Layout getLayout();
method public float getLetterSpacing();
method public int getLineBounds(int, android.graphics.Rect);
@@ -52470,6 +52642,7 @@
method public void setExtractedText(android.view.inputmethod.ExtractedText);
method public void setFallbackLineSpacing(boolean);
method public void setFilters(android.text.InputFilter[]);
+ method public void setFirstBaselineToTopHeight(int);
method public void setFontFeatureSettings(java.lang.String);
method public boolean setFontVariationSettings(java.lang.String);
method protected boolean setFrame(int, int, int, int);
@@ -52491,7 +52664,9 @@
method public void setInputType(int);
method public void setJustificationMode(int);
method public void setKeyListener(android.text.method.KeyListener);
+ method public void setLastBaselineToBottomHeight(int);
method public void setLetterSpacing(float);
+ method public void setLineHeight(int);
method public void setLineSpacing(float, float);
method public void setLines(int);
method public final void setLinkTextColor(int);
diff --git a/api/removed.txt b/api/removed.txt
index 2aab223..77088e5 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -265,6 +265,7 @@
method public android.graphics.drawable.Drawable getBadgedDrawableForUser(android.graphics.drawable.Drawable, android.os.UserHandle, android.graphics.Rect, int);
method public android.graphics.drawable.Drawable getBadgedIconForUser(android.graphics.drawable.Drawable, android.os.UserHandle);
method public java.lang.CharSequence getBadgedLabelForUser(java.lang.CharSequence, android.os.UserHandle);
+ method public deprecated boolean trySetQuietModeEnabled(boolean, android.os.UserHandle);
}
}
diff --git a/api/system-current.txt b/api/system-current.txt
index b2d5a49..6390de6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -33,6 +33,7 @@
field public static final java.lang.String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE";
field public static final java.lang.String BLUETOOTH_PRIVILEGED = "android.permission.BLUETOOTH_PRIVILEGED";
field public static final java.lang.String BRICK = "android.permission.BRICK";
+ field public static final java.lang.String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
field public static final deprecated java.lang.String BROADCAST_NETWORK_PRIVILEGED = "android.permission.BROADCAST_NETWORK_PRIVILEGED";
field public static final java.lang.String CALL_PRIVILEGED = "android.permission.CALL_PRIVILEGED";
field public static final java.lang.String CAMERA_DISABLE_TRANSMIT_LED = "android.permission.CAMERA_DISABLE_TRANSMIT_LED";
@@ -46,6 +47,7 @@
field public static final java.lang.String CHANGE_CONFIGURATION = "android.permission.CHANGE_CONFIGURATION";
field public static final java.lang.String CHANGE_DEVICE_IDLE_TEMP_WHITELIST = "android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST";
field public static final java.lang.String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
+ field public static final java.lang.String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
field public static final java.lang.String CONNECTIVITY_INTERNAL = "android.permission.CONNECTIVITY_INTERNAL";
field public static final java.lang.String CONNECTIVITY_USE_RESTRICTED_NETWORKS = "android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS";
field public static final java.lang.String CONTROL_INCALL_EXPERIENCE = "android.permission.CONTROL_INCALL_EXPERIENCE";
@@ -153,6 +155,7 @@
field public static final java.lang.String SET_ALWAYS_FINISH = "android.permission.SET_ALWAYS_FINISH";
field public static final java.lang.String SET_ANIMATION_SCALE = "android.permission.SET_ANIMATION_SCALE";
field public static final java.lang.String SET_DEBUG_APP = "android.permission.SET_DEBUG_APP";
+ field public static final java.lang.String SET_HARMFUL_APP_WARNINGS = "android.permission.SET_HARMFUL_APP_WARNINGS";
field public static final java.lang.String SET_MEDIA_KEY_LISTENER = "android.permission.SET_MEDIA_KEY_LISTENER";
field public static final java.lang.String SET_ORIENTATION = "android.permission.SET_ORIENTATION";
field public static final java.lang.String SET_POINTER_SPEED = "android.permission.SET_POINTER_SPEED";
@@ -161,6 +164,7 @@
field public static final java.lang.String SET_TIME = "android.permission.SET_TIME";
field public static final java.lang.String SET_VOLUME_KEY_LONG_PRESS_LISTENER = "android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER";
field public static final java.lang.String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT";
+ field public static final java.lang.String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE";
field public static final java.lang.String SHUTDOWN = "android.permission.SHUTDOWN";
field public static final java.lang.String SIGNAL_PERSISTENT_PROCESSES = "android.permission.SIGNAL_PERSISTENT_PROCESSES";
field public static final java.lang.String STATUS_BAR = "android.permission.STATUS_BAR";
@@ -190,10 +194,6 @@
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
- field public static final int searchKeyphrase = 16843871; // 0x101045f
- field public static final int searchKeyphraseId = 16843870; // 0x101045e
- field public static final int searchKeyphraseRecognitionFlags = 16843942; // 0x10104a6
- field public static final int searchKeyphraseSupportedLocales = 16843872; // 0x1010460
}
public static final class R.raw {
@@ -315,6 +315,11 @@
method public void onInstantAppResolveInfo(java.util.List<android.content.pm.InstantAppResolveInfo>);
}
+ public class KeyguardManager {
+ method public android.content.Intent createConfirmFactoryResetCredentialIntent(java.lang.CharSequence, java.lang.CharSequence, java.lang.CharSequence);
+ method public void requestDismissKeyguard(android.app.Activity, java.lang.CharSequence, android.app.KeyguardManager.KeyguardDismissCallback);
+ }
+
public class Notification implements android.os.Parcelable {
field public static final java.lang.String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
field public static final java.lang.String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
@@ -425,6 +430,7 @@
method public java.lang.String getCurrentTransport();
method public boolean isAppEligibleForBackup(java.lang.String);
method public boolean isBackupEnabled();
+ method public boolean isBackupServiceActive(android.os.UserHandle);
method public java.lang.String[] listAllTransports();
method public int requestBackup(java.lang.String[], android.app.backup.BackupObserver);
method public int requestBackup(java.lang.String[], android.app.backup.BackupObserver, android.app.backup.BackupManagerMonitor, int);
@@ -684,6 +690,9 @@
method public boolean isEncrypted();
method public boolean removeBond();
method public boolean setPhonebookAccessPermission(int);
+ field public static final int ACCESS_ALLOWED = 1; // 0x1
+ field public static final int ACCESS_REJECTED = 2; // 0x2
+ field public static final int ACCESS_UNKNOWN = 0; // 0x0
}
public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
@@ -692,6 +701,11 @@
method public boolean setPriority(android.bluetooth.BluetoothDevice, int);
}
+ public abstract interface BluetoothProfile {
+ field public static final int PRIORITY_OFF = 0; // 0x0
+ field public static final int PRIORITY_ON = 100; // 0x64
+ }
+
}
package android.bluetooth.le {
@@ -896,6 +910,7 @@
method public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(java.lang.String);
method public android.content.pm.dex.ArtManager getArtManager();
method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
+ method public java.lang.CharSequence getHarmfulAppWarning(java.lang.String);
method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
method public abstract android.graphics.drawable.Drawable getInstantAppIcon(java.lang.String);
method public abstract android.content.ComponentName getInstantAppInstallerComponent();
@@ -912,6 +927,7 @@
method public abstract void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
+ method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence);
method public abstract void setUpdateAvailable(java.lang.String, boolean);
method public abstract boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
@@ -1079,8 +1095,38 @@
package android.hardware.display {
+ public final class BrightnessChangeEvent implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessChangeEvent> CREATOR;
+ field public final float batteryLevel;
+ field public final float brightness;
+ field public final int colorTemperature;
+ field public final float lastBrightness;
+ field public final long[] luxTimestamps;
+ field public final float[] luxValues;
+ field public final boolean nightMode;
+ field public final java.lang.String packageName;
+ field public final long timeStamp;
+ }
+
+ public final class BrightnessConfiguration implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.util.Pair<float[], float[]> getCurve();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessConfiguration> CREATOR;
+ }
+
+ public static class BrightnessConfiguration.Builder {
+ ctor public BrightnessConfiguration.Builder();
+ method public android.hardware.display.BrightnessConfiguration build();
+ method public android.hardware.display.BrightnessConfiguration.Builder setCurve(float[], float[]);
+ }
+
public final class DisplayManager {
+ method public java.util.List<android.hardware.display.BrightnessChangeEvent> getBrightnessEvents();
method public android.graphics.Point getStableDisplaySize();
+ method public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
}
}
@@ -1688,6 +1734,39 @@
package android.hardware.radio {
+ public final class ProgramList implements java.lang.AutoCloseable {
+ method public void addOnCompleteListener(java.util.concurrent.Executor, android.hardware.radio.ProgramList.OnCompleteListener);
+ method public void addOnCompleteListener(android.hardware.radio.ProgramList.OnCompleteListener);
+ method public void close();
+ method public android.hardware.radio.RadioManager.ProgramInfo get(android.hardware.radio.ProgramSelector.Identifier);
+ method public void registerListCallback(java.util.concurrent.Executor, android.hardware.radio.ProgramList.ListCallback);
+ method public void registerListCallback(android.hardware.radio.ProgramList.ListCallback);
+ method public void removeOnCompleteListener(android.hardware.radio.ProgramList.OnCompleteListener);
+ method public java.util.List<android.hardware.radio.RadioManager.ProgramInfo> toList();
+ method public void unregisterListCallback(android.hardware.radio.ProgramList.ListCallback);
+ }
+
+ public static final class ProgramList.Filter implements android.os.Parcelable {
+ ctor public ProgramList.Filter(java.util.Set<java.lang.Integer>, java.util.Set<android.hardware.radio.ProgramSelector.Identifier>, boolean, boolean);
+ method public boolean areCategoriesIncluded();
+ method public boolean areModificationsExcluded();
+ method public int describeContents();
+ method public java.util.Set<java.lang.Integer> getIdentifierTypes();
+ method public java.util.Set<android.hardware.radio.ProgramSelector.Identifier> getIdentifiers();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.radio.ProgramList.Filter> CREATOR;
+ }
+
+ public static abstract class ProgramList.ListCallback {
+ ctor public ProgramList.ListCallback();
+ method public void onItemChanged(android.hardware.radio.ProgramSelector.Identifier);
+ method public void onItemRemoved(android.hardware.radio.ProgramSelector.Identifier);
+ }
+
+ public static abstract interface ProgramList.OnCompleteListener {
+ method public abstract void onComplete();
+ }
+
public final class ProgramSelector implements android.os.Parcelable {
ctor public ProgramSelector(int, android.hardware.radio.ProgramSelector.Identifier, android.hardware.radio.ProgramSelector.Identifier[], long[]);
method public static android.hardware.radio.ProgramSelector createAmFmSelector(int, int);
@@ -1711,6 +1790,7 @@
field public static final int IDENTIFIER_TYPE_DRMO_SERVICE_ID = 9; // 0x9
field public static final int IDENTIFIER_TYPE_HD_STATION_ID_EXT = 3; // 0x3
field public static final int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4; // 0x4
+ field public static final int IDENTIFIER_TYPE_INVALID = 0; // 0x0
field public static final int IDENTIFIER_TYPE_RDS_PI = 2; // 0x2
field public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13; // 0xd
field public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12; // 0xc
@@ -1722,6 +1802,7 @@
field public static final int PROGRAM_TYPE_DRMO = 6; // 0x6
field public static final int PROGRAM_TYPE_FM = 2; // 0x2
field public static final int PROGRAM_TYPE_FM_HD = 4; // 0x4
+ field public static final int PROGRAM_TYPE_INVALID = 0; // 0x0
field public static final int PROGRAM_TYPE_SXM = 7; // 0x7
field public static final int PROGRAM_TYPE_VENDOR_END = 1999; // 0x7cf
field public static final int PROGRAM_TYPE_VENDOR_START = 1000; // 0x3e8
@@ -1940,10 +2021,11 @@
method public abstract void cancelAnnouncement();
method public abstract void close();
method public abstract int getConfiguration(android.hardware.radio.RadioManager.BandConfig[]);
+ method public android.hardware.radio.ProgramList getDynamicProgramList(android.hardware.radio.ProgramList.Filter);
method public abstract boolean getMute();
method public java.util.Map<java.lang.String, java.lang.String> getParameters(java.util.List<java.lang.String>);
method public abstract int getProgramInformation(android.hardware.radio.RadioManager.ProgramInfo[]);
- method public abstract java.util.List<android.hardware.radio.RadioManager.ProgramInfo> getProgramList(java.util.Map<java.lang.String, java.lang.String>);
+ method public abstract deprecated java.util.List<android.hardware.radio.RadioManager.ProgramInfo> getProgramList(java.util.Map<java.lang.String, java.lang.String>);
method public abstract boolean hasControl();
method public abstract deprecated boolean isAnalogForced();
method public abstract boolean isAntennaConnected();
@@ -3475,6 +3557,7 @@
method public boolean isRestrictedProfile();
field public static final java.lang.String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
field public static final deprecated java.lang.String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
+ field public static final java.lang.String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background";
field public static final int RESTRICTION_NOT_SET = 0; // 0x0
field public static final int RESTRICTION_SOURCE_DEVICE_OWNER = 2; // 0x2
field public static final int RESTRICTION_SOURCE_PROFILE_OWNER = 4; // 0x4
@@ -3991,6 +4074,7 @@
method public final void removeEscrowToken(long, android.os.UserHandle);
method public final void revokeTrust();
method public final void setManagingTrust(boolean);
+ method public final void showKeyguardErrorMessage(java.lang.CharSequence);
method public final void unlockUserWithToken(long, byte[], android.os.UserHandle);
field public static final int FLAG_GRANT_TRUST_DISMISS_KEYGUARD = 2; // 0x2
field public static final int FLAG_GRANT_TRUST_INITIATED_BY_USER = 1; // 0x1
@@ -4275,6 +4359,7 @@
method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
method public void setSubscriptionPlans(int, java.util.List<android.telephony.SubscriptionPlan>);
field public static final java.lang.String ACTION_MANAGE_SUBSCRIPTION_PLANS = "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS";
+ field public static final java.lang.String ACTION_REFRESH_SUBSCRIPTION_PLANS = "android.telephony.action.REFRESH_SUBSCRIPTION_PLANS";
}
public final class SubscriptionPlan implements android.os.Parcelable {
@@ -4371,6 +4456,8 @@
method public deprecated void setDataEnabled(int, boolean);
method public boolean setRadio(boolean);
method public boolean setRadioPower(boolean);
+ method public void setSimPowerState(int);
+ method public void setSimPowerStateForSlot(int, int);
method public deprecated void setVisualVoicemailEnabled(android.telecom.PhoneAccountHandle, boolean);
method public void setVoiceActivationState(int);
method public deprecated void silenceRinger();
@@ -4403,11 +4490,11 @@
package android.telephony.data {
public final class DataCallResponse implements android.os.Parcelable {
- ctor public DataCallResponse(int, int, int, int, java.lang.String, java.lang.String, java.util.List<android.telephony.data.InterfaceAddress>, java.util.List<java.net.InetAddress>, java.util.List<java.net.InetAddress>, java.util.List<java.lang.String>, int);
+ ctor public DataCallResponse(int, int, int, int, java.lang.String, java.lang.String, java.util.List<android.net.LinkAddress>, java.util.List<java.net.InetAddress>, java.util.List<java.net.InetAddress>, java.util.List<java.lang.String>, int);
ctor public DataCallResponse(android.os.Parcel);
method public int describeContents();
method public int getActive();
- method public java.util.List<android.telephony.data.InterfaceAddress> getAddresses();
+ method public java.util.List<android.net.LinkAddress> getAddresses();
method public int getCallId();
method public java.util.List<java.net.InetAddress> getDnses();
method public java.util.List<java.net.InetAddress> getGateways();
@@ -4450,15 +4537,36 @@
field public static final int TYPE_COMMON = 0; // 0x0
}
- public final class InterfaceAddress implements android.os.Parcelable {
- ctor public InterfaceAddress(java.net.InetAddress, int);
- ctor public InterfaceAddress(java.lang.String, int) throws java.net.UnknownHostException;
- ctor public InterfaceAddress(android.os.Parcel);
- method public int describeContents();
- method public java.net.InetAddress getAddress();
- method public int getNetworkPrefixLength();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telephony.data.InterfaceAddress> CREATOR;
+ public abstract class DataService extends android.app.Service {
+ method public abstract android.telephony.data.DataService.DataServiceProvider createDataServiceProvider(int);
+ field public static final java.lang.String DATA_SERVICE_EXTRA_SLOT_ID = "android.telephony.data.extra.SLOT_ID";
+ field public static final java.lang.String DATA_SERVICE_INTERFACE = "android.telephony.data.DataService";
+ }
+
+ public class DataService.DataServiceProvider {
+ ctor public DataService.DataServiceProvider(int);
+ method public void deactivateDataCall(int, boolean, boolean, android.telephony.data.DataServiceCallback);
+ method public void getDataCallList(android.telephony.data.DataServiceCallback);
+ method public final int getSlotId();
+ method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
+ method protected void onDestroy();
+ method public void setDataProfile(java.util.List<android.telephony.data.DataProfile>, boolean, android.telephony.data.DataServiceCallback);
+ method public void setInitialAttachApn(android.telephony.data.DataProfile, boolean, android.telephony.data.DataServiceCallback);
+ method public void setupDataCall(int, android.telephony.data.DataProfile, boolean, boolean, boolean, android.net.LinkProperties, android.telephony.data.DataServiceCallback);
+ }
+
+ public class DataServiceCallback {
+ method public void onDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
+ method public void onDeactivateDataCallComplete(int);
+ method public void onGetDataCallListComplete(int, java.util.List<android.telephony.data.DataCallResponse>);
+ method public void onSetDataProfileComplete(int);
+ method public void onSetInitialAttachApnComplete(int);
+ method public void onSetupDataCallComplete(int, android.telephony.data.DataCallResponse);
+ field public static final int RESULT_ERROR_BUSY = 3; // 0x3
+ field public static final int RESULT_ERROR_ILLEGAL_STATE = 4; // 0x4
+ field public static final int RESULT_ERROR_INVALID_ARG = 2; // 0x2
+ field public static final int RESULT_ERROR_UNSUPPORTED = 1; // 0x1
+ field public static final int RESULT_SUCCESS = 0; // 0x0
}
}
@@ -4573,9 +4681,12 @@
}
public final class StatsManager {
+ method public boolean addConfiguration(java.lang.String, byte[], java.lang.String, java.lang.String);
method public boolean addConfiguration(long, byte[], java.lang.String, java.lang.String);
+ method public byte[] getData(java.lang.String);
method public byte[] getData(long);
method public byte[] getMetadata();
+ method public boolean removeConfiguration(java.lang.String);
method public boolean removeConfiguration(long);
}
diff --git a/api/system-removed.txt b/api/system-removed.txt
index a3bf576..b63703d 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -126,25 +126,6 @@
field public boolean untrusted;
}
- public class WifiConnectionStatistics implements android.os.Parcelable {
- ctor public WifiConnectionStatistics();
- ctor public WifiConnectionStatistics(android.net.wifi.WifiConnectionStatistics);
- method public int describeContents();
- method public void incrementOrAddUntrusted(java.lang.String, int, int);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.net.wifi.WifiConnectionStatistics> CREATOR;
- field public int num24GhzConnected;
- field public int num5GhzConnected;
- field public int numAutoJoinAttempt;
- field public int numAutoRoamAttempt;
- field public int numWifiManagerJoinAttempt;
- field public java.util.HashMap<java.lang.String, android.net.wifi.WifiNetworkConnectionStatistics> untrustedNetworkHistory;
- }
-
- public class WifiManager {
- method public android.net.wifi.WifiConnectionStatistics getConnectionStatistics();
- }
-
}
package android.os {
diff --git a/api/test-current.txt b/api/test-current.txt
index b16e700..6941731 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -156,6 +156,7 @@
method public boolean isDeviceManaged();
field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED";
field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED";
+ field public static final java.lang.String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
field public static final java.lang.String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION";
}
@@ -294,6 +295,43 @@
}
+package android.hardware.display {
+
+ public final class BrightnessChangeEvent implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessChangeEvent> CREATOR;
+ field public final float batteryLevel;
+ field public final float brightness;
+ field public final int colorTemperature;
+ field public final float lastBrightness;
+ field public final long[] luxTimestamps;
+ field public final float[] luxValues;
+ field public final boolean nightMode;
+ field public final java.lang.String packageName;
+ field public final long timeStamp;
+ }
+
+ public final class BrightnessConfiguration implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.util.Pair<float[], float[]> getCurve();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessConfiguration> CREATOR;
+ }
+
+ public static class BrightnessConfiguration.Builder {
+ ctor public BrightnessConfiguration.Builder();
+ method public android.hardware.display.BrightnessConfiguration build();
+ method public android.hardware.display.BrightnessConfiguration.Builder setCurve(float[], float[]);
+ }
+
+ public final class DisplayManager {
+ method public java.util.List<android.hardware.display.BrightnessChangeEvent> getBrightnessEvents();
+ method public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
+ }
+
+}
+
package android.location {
public final class GnssClock implements android.os.Parcelable {
@@ -483,6 +521,7 @@
}
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
+ field public static final java.lang.String LOW_POWER_MODE = "low_power";
field public static final java.lang.String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package";
}
diff --git a/cmds/incidentd/incidentd.rc b/cmds/incidentd/incidentd.rc
index fe38a71..66667dc 100644
--- a/cmds/incidentd/incidentd.rc
+++ b/cmds/incidentd/incidentd.rc
@@ -14,6 +14,8 @@
service incidentd /system/bin/incidentd
class main
+ user incidentd
+ group incidentd log
on post-fs-data
# Create directory for incidentd
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index ffe652f..2c35d41 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -186,7 +186,8 @@
tests/statsd_test_util.cpp \
tests/e2e/WakelockDuration_e2e_test.cpp \
tests/e2e/MetricConditionLink_e2e_test.cpp \
- tests/e2e/Attribution_e2e_test.cpp
+ tests/e2e/Attribution_e2e_test.cpp \
+ tests/e2e/GaugeMetric_e2e_test.cpp
LOCAL_STATIC_LIBRARIES := \
$(statsd_common_static_libraries) \
@@ -198,6 +199,24 @@
include $(BUILD_NATIVE_TEST)
+##############################
+# stats proto static java lib
+##############################
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := statsdprotolite
+
+LOCAL_SRC_FILES := \
+ src/stats_log.proto \
+ src/statsd_config.proto \
+ src/atoms.proto
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := lite
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ platformprotoslite
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
statsd_common_src:=
statsd_common_aidl_includes:=
@@ -208,4 +227,4 @@
##############################
-include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 1cfec32..9d6d8a1 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -301,7 +301,6 @@
}
void StatsLogProcessor::WriteDataToDisk() {
- mkdir(STATS_DATA_DIR, S_IRWXU);
std::lock_guard<std::mutex> lock(mMetricsMutex);
for (auto& pair : mMetricsManagers) {
const ConfigKey& key = pair.first;
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 09e10a1..301e3a5 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -99,6 +99,7 @@
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions);
FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice);
+ FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
};
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 7f77ef7..4a7f0c4 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -102,7 +102,6 @@
CpuTimePerUidFreq cpu_time_per_uid_freq = 10010;
WifiActivityEnergyInfo wifi_activity_energy_info = 10011;
ModemActivityInfo modem_activity_info = 10012;
- AttributionChainDummyAtom attribution_chain_dummy_atom = 100000;
}
}
@@ -147,11 +146,6 @@
* *****************************************************************************
*/
-message AttributionChainDummyAtom {
- repeated AttributionNode attribution_node = 1;
- optional int32 value = 2;
-}
-
/**
* Logs when the screen state changes.
*
@@ -218,8 +212,7 @@
* frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message BleScanStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
+ repeated AttributionNode attribution_node = 1;
enum State {
OFF = 0;
@@ -236,8 +229,7 @@
*/
// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats).
message BleUnoptimizedScanStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
+ repeated AttributionNode attribution_node = 1;
enum State {
OFF = 0;
@@ -254,8 +246,7 @@
*/
// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats).
message BleScanResultReceived {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
+ repeated AttributionNode attribution_node = 1;
// Number of ble scan results returned.
optional int32 num_of_results = 2;
@@ -268,8 +259,7 @@
* frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message SensorStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
+ repeated AttributionNode attribution_node = 1;
// TODO: Is there a way to get the actual name of the sensor?
// The id (int) of the sensor.
@@ -290,8 +280,7 @@
* frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message GpsScanStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
+ repeated AttributionNode attribution_node = 1;
enum State {
OFF = 0;
@@ -308,8 +297,7 @@
* frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message SyncStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
+ repeated AttributionNode attribution_node = 1;
// Name of the sync (as named in the app)
optional string name = 2;
@@ -328,8 +316,7 @@
* frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message ScheduledJobStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
+ repeated AttributionNode attribution_node = 1;
// Name of the job (as named in the app)
optional string name = 2;
@@ -350,8 +337,7 @@
* frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message AudioStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
+ repeated AttributionNode attribution_node = 1;
enum State {
OFF = 0;
@@ -367,8 +353,7 @@
* frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message MediaCodecActivityChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
+ repeated AttributionNode attribution_node = 1;
enum State {
OFF = 0;
@@ -384,8 +369,7 @@
* frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message FlashlightStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
+ repeated AttributionNode attribution_node = 1;
enum State {
OFF = 0;
@@ -401,8 +385,7 @@
* frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message CameraStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
+ repeated AttributionNode attribution_node = 1;
enum State {
OFF = 0;
@@ -447,8 +430,7 @@
* frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message LongPartialWakelockStateChanged {
- // TODO: Add attribution instead of uid?
- optional int32 uid = 1;
+ repeated AttributionNode attribution_node = 1;
// The wakelock tag (Called tag in the Java API, sometimes name elsewhere).
optional string tag = 2;
@@ -605,8 +587,7 @@
* frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
*/
message WakeupAlarmOccurred {
- // TODO: Add attribution instead of uid?
- optional int32 uid = 1;
+ repeated AttributionNode attribution_node = 1;
// Name of the wakeup alarm.
optional string tag = 2;
@@ -673,8 +654,7 @@
* frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message WifiLockStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
+ repeated AttributionNode attribution_node = 1;
enum State {
OFF = 0;
@@ -708,8 +688,7 @@
* frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message WifiScanStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
+ repeated AttributionNode attribution_node = 1;
enum State {
OFF = 0;
@@ -1109,14 +1088,10 @@
/**
* Pulls Cpu time per frequency.
- * Note: this should be pulled for gauge metric only, without condition.
- * The puller keeps internal state of last values. It should not be pulled by
- * different metrics.
- * The pulled data is delta of cpu time from last pull, calculated as
- * following:
- * if current time is larger than last value, take delta between the two.
- * if current time is smaller than last value, there must be a cpu
- * hotplug event, and the current time is taken as delta.
+ * Pulls the time the cpu spend on the frequency index. Frequency index
+ * starts from highest to lowest. The value should be monotonically
+ * increasing since boot. However, if there is a cpu
+ * hotplug event, the value would be reset as well.
*/
message CpuTimePerFreq {
optional uint32 cluster = 1;
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 184f69e..1481921 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -181,8 +181,6 @@
}
void ConfigManager::update_saved_configs(const ConfigKey& key, const StatsdConfig& config) {
- mkdir(STATS_SERVICE_DIR, S_IRWXU);
-
// If there is a pre-existing config with same key we should first delete it.
remove_saved_configs(key);
@@ -257,7 +255,7 @@
metric->set_id(2); // "METRIC_2"
metric->set_what(104);
metric->set_bucket(ONE_MINUTE);
- FieldMatcher* dimensions = metric->mutable_dimensions();
+ FieldMatcher* dimensions = metric->mutable_dimensions_in_what();
dimensions->set_field(UID_PROCESS_STATE_TAG_ID);
dimensions->add_child()->set_field(UID_PROCESS_STATE_UID_KEY);
@@ -280,7 +278,7 @@
metric->set_what(104);
metric->set_bucket(ONE_MINUTE);
- dimensions = metric->mutable_dimensions();
+ dimensions = metric->mutable_dimensions_in_what();
dimensions->set_field(UID_PROCESS_STATE_TAG_ID);
dimensions->add_child()->set_field(UID_PROCESS_STATE_UID_KEY);
metric->set_condition(202);
@@ -290,7 +288,7 @@
metric->set_id(4);
metric->set_what(107);
metric->set_bucket(ONE_MINUTE);
- dimensions = metric->mutable_dimensions();
+ dimensions = metric->mutable_dimensions_in_what();
dimensions->set_field(WAKE_LOCK_TAG_ID);
dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
@@ -308,7 +306,7 @@
durationMetric->set_id(5);
durationMetric->set_bucket(ONE_MINUTE);
durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
- dimensions = durationMetric->mutable_dimensions();
+ dimensions = durationMetric->mutable_dimensions_in_what();
dimensions->set_field(WAKE_LOCK_TAG_ID);
dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
durationMetric->set_what(205);
@@ -325,7 +323,7 @@
durationMetric->set_id(6);
durationMetric->set_bucket(ONE_MINUTE);
durationMetric->set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
- dimensions = durationMetric->mutable_dimensions();
+ dimensions = durationMetric->mutable_dimensions_in_what();
dimensions->set_field(WAKE_LOCK_TAG_ID);
dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
durationMetric->set_what(205);
@@ -378,7 +376,7 @@
valueMetric->mutable_value_field()->set_field(KERNEL_WAKELOCK_TAG_ID);
valueMetric->mutable_value_field()->add_child()->set_field(KERNEL_WAKELOCK_COUNT_KEY);
valueMetric->set_condition(201);
- dimensions = valueMetric->mutable_dimensions();
+ dimensions = valueMetric->mutable_dimensions_in_what();
dimensions->set_field(KERNEL_WAKELOCK_TAG_ID);
dimensions->add_child()->set_field(KERNEL_WAKELOCK_NAME_KEY);
// This is for testing easier. We should never set bucket size this small.
diff --git a/cmds/statsd/src/dimension.h b/cmds/statsd/src/dimension.h
index 845c138..d0f96a2 100644
--- a/cmds/statsd/src/dimension.h
+++ b/cmds/statsd/src/dimension.h
@@ -32,6 +32,10 @@
const DimensionsValue* getSingleLeafValue(const DimensionsValue* value);
DimensionsValue getSingleLeafValue(const DimensionsValue& value);
+// Appends the leaf node to the parent tree.
+void appendLeafNodeToParent(const Field& field, const DimensionsValue& value,
+ DimensionsValue* parentValue);
+
// Constructs the DimensionsValue protos from the FieldMatcher. Each DimensionsValue proto
// represents a tree. When the input proto has repeated fields and the input "dimensions" wants
// "ANY" locations, it will return multiple trees.
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 3e98098..ef27210 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -50,8 +50,9 @@
// for CountMetricDataWrapper
const int FIELD_ID_DATA = 1;
// for CountMetricData
-const int FIELD_ID_DIMENSION = 1;
-const int FIELD_ID_BUCKET_INFO = 2;
+const int FIELD_ID_DIMENSION_IN_WHAT = 1;
+const int FIELD_ID_DIMENSION_IN_CONDITION = 2;
+const int FIELD_ID_BUCKET_INFO = 3;
// for CountBucketInfo
const int FIELD_ID_START_BUCKET_NANOS = 1;
const int FIELD_ID_END_BUCKET_NANOS = 2;
@@ -70,7 +71,7 @@
}
// TODO: use UidMap if uid->pkg_name is required
- mDimensions = metric.dimensions();
+ mDimensions = metric.dimensions_in_what();
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
@@ -98,7 +99,7 @@
auto count_metrics = report->mutable_count_metrics();
for (const auto& counter : mPastBuckets) {
CountMetricData* metricData = count_metrics->add_data();
- *metricData->mutable_dimension() = counter.first.getDimensionsValue();
+ *metricData->mutable_dimensions_in_what() = counter.first.getDimensionsValue();
for (const auto& bucket : counter.second) {
CountBucketInfo* bucketInfo = metricData->add_bucket_info();
bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
@@ -127,7 +128,7 @@
// First fill dimension.
long long dimensionToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
+ FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
protoOutput->end(dimensionToken);
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index b546297..58dd464 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -49,8 +49,9 @@
// for DurationMetricDataWrapper
const int FIELD_ID_DATA = 1;
// for DurationMetricData
-const int FIELD_ID_DIMENSION = 1;
-const int FIELD_ID_BUCKET_INFO = 2;
+const int FIELD_ID_DIMENSION_IN_WHAT = 1;
+const int FIELD_ID_DIMENSION_IN_CONDITION = 2;
+const int FIELD_ID_BUCKET_INFO = 3;
// for DurationBucketInfo
const int FIELD_ID_START_BUCKET_NANOS = 1;
const int FIELD_ID_END_BUCKET_NANOS = 2;
@@ -80,7 +81,7 @@
}
// TODO: use UidMap if uid->pkg_name is required
- mDimensions = metric.dimensions();
+ mDimensions = metric.dimensions_in_what();
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
@@ -154,7 +155,7 @@
auto duration_metrics = report->mutable_duration_metrics();
for (const auto& pair : mPastBuckets) {
DurationMetricData* metricData = duration_metrics->add_data();
- *metricData->mutable_dimension() = pair.first.getDimensionsValue();
+ *metricData->mutable_dimensions_in_what() = pair.first.getDimensionsValue();
for (const auto& bucket : pair.second) {
auto bucketInfo = metricData->add_bucket_info();
bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
@@ -183,7 +184,7 @@
// First fill dimension.
long long dimensionToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
+ FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
protoOutput->end(dimensionToken);
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 1a4888c..17305e3 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -51,8 +51,9 @@
// for GaugeMetricDataWrapper
const int FIELD_ID_DATA = 1;
// for GaugeMetricData
-const int FIELD_ID_DIMENSION = 1;
-const int FIELD_ID_BUCKET_INFO = 2;
+const int FIELD_ID_DIMENSION_IN_WHAT = 1;
+const int FIELD_ID_DIMENSION_IN_CONDITION = 2;
+const int FIELD_ID_BUCKET_INFO = 3;
// for GaugeBucketInfo
const int FIELD_ID_START_BUCKET_NANOS = 1;
const int FIELD_ID_END_BUCKET_NANOS = 2;
@@ -79,7 +80,7 @@
mFieldFilter = metric.gauge_fields_filter();
// TODO: use UidMap if uid->pkg_name is required
- mDimensions = metric.dimensions();
+ mDimensions = metric.dimensions_in_what();
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
@@ -114,6 +115,9 @@
void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
flushIfNeededLocked(dumpTimeNs);
+ ProtoOutputStream pbOutput;
+ onDumpReportLocked(dumpTimeNs, &pbOutput);
+ parseProtoOutputStream(pbOutput, report);
}
void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
@@ -135,7 +139,7 @@
// First fill dimension.
long long dimensionToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
+ FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
protoOutput->end(dimensionToken);
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 08b0981..a0239fc 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -141,6 +141,7 @@
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions);
FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice);
+ FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 74bd6f9..c7550f7 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -54,8 +54,9 @@
// for ValueMetricDataWrapper
const int FIELD_ID_DATA = 1;
// for ValueMetricData
-const int FIELD_ID_DIMENSION = 1;
-const int FIELD_ID_BUCKET_INFO = 2;
+const int FIELD_ID_DIMENSION_IN_WHAT = 1;
+const int FIELD_ID_DIMENSION_IN_CONDITION = 2;
+const int FIELD_ID_BUCKET_INFO = 3;
// for ValueBucketInfo
const int FIELD_ID_START_BUCKET_NANOS = 1;
const int FIELD_ID_END_BUCKET_NANOS = 2;
@@ -80,7 +81,7 @@
}
mBucketSizeNs = bucketSizeMills * 1000000;
- mDimensions = metric.dimensions();
+ mDimensions = metric.dimensions_in_what();
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
@@ -123,7 +124,7 @@
auto value_metrics = report->mutable_value_metrics();
for (const auto& pair : mPastBuckets) {
ValueMetricData* metricData = value_metrics->add_data();
- *metricData->mutable_dimension() = pair.first.getDimensionsValue();
+ *metricData->mutable_dimensions_in_what() = pair.first.getDimensionsValue();
for (const auto& bucket : pair.second) {
ValueBucketInfo* bucketInfo = metricData->add_bucket_info();
bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
@@ -149,7 +150,7 @@
// First fill dimension.
long long dimensionToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
+ FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
protoOutput->end(dimensionToken);
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index bc887ac..a0173ee 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -227,7 +227,8 @@
int metricIndex = allMetricProducers.size();
metricMap.insert({metric.id(), metricIndex});
int trackerIndex;
- if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.has_dimensions(),
+ if (!handleMetricWithLogTrackers(metric.what(), metricIndex,
+ metric.has_dimensions_in_what(),
allAtomMatchers, logTrackerMap, trackerToMetricMap,
trackerIndex)) {
return false;
@@ -279,7 +280,7 @@
int trackerIndices[3] = {-1, -1, -1};
if (!simplePredicate.has_start() ||
!handleMetricWithLogTrackers(simplePredicate.start(), metricIndex,
- metric.has_dimensions(), allAtomMatchers,
+ metric.has_dimensions_in_what(), allAtomMatchers,
logTrackerMap, trackerToMetricMap, trackerIndices[0])) {
ALOGE("Duration metrics must specify a valid the start event matcher");
return false;
@@ -287,14 +288,14 @@
if (simplePredicate.has_stop() &&
!handleMetricWithLogTrackers(simplePredicate.stop(), metricIndex,
- metric.has_dimensions(), allAtomMatchers,
+ metric.has_dimensions_in_what(), allAtomMatchers,
logTrackerMap, trackerToMetricMap, trackerIndices[1])) {
return false;
}
if (simplePredicate.has_stop_all() &&
!handleMetricWithLogTrackers(simplePredicate.stop_all(), metricIndex,
- metric.has_dimensions(), allAtomMatchers,
+ metric.has_dimensions_in_what(), allAtomMatchers,
logTrackerMap, trackerToMetricMap, trackerIndices[2])) {
return false;
}
@@ -371,7 +372,8 @@
int metricIndex = allMetricProducers.size();
metricMap.insert({metric.id(), metricIndex});
int trackerIndex;
- if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.has_dimensions(),
+ if (!handleMetricWithLogTrackers(metric.what(), metricIndex,
+ metric.has_dimensions_in_what(),
allAtomMatchers, logTrackerMap, trackerToMetricMap,
trackerIndex)) {
return false;
@@ -429,7 +431,8 @@
int metricIndex = allMetricProducers.size();
metricMap.insert({metric.id(), metricIndex});
int trackerIndex;
- if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.has_dimensions(),
+ if (!handleMetricWithLogTrackers(metric.what(), metricIndex,
+ metric.has_dimensions_in_what(),
allAtomMatchers, logTrackerMap, trackerToMetricMap,
trackerIndex)) {
return false;
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index b0c3197..dcb8eed 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -471,10 +471,13 @@
{"AID_AUTOMOTIVE_EVS", 1062},
{"AID_LOWPAN", 1063},
{"AID_HSM", 1064},
+ {"AID_RESERVED_DISK", 1065},
+ {"AID_STATSD", 1066},
+ {"AID_INCIDENTD", 1067},
{"AID_SHELL", 2000},
{"AID_CACHE", 2001},
{"AID_DIAG", 2002}};
} // namespace statsd
} // namespace os
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 2596a5f..393f795 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -56,7 +56,9 @@
}
message CountMetricData {
- optional DimensionsValue dimension = 1;
+ optional DimensionsValue dimensions_in_what = 1;
+
+ optional DimensionsValue dimensions_in_condition = 3;
repeated CountBucketInfo bucket_info = 2;
}
@@ -70,7 +72,9 @@
}
message DurationMetricData {
- optional DimensionsValue dimension = 1;
+ optional DimensionsValue dimensions_in_what = 1;
+
+ optional DimensionsValue dimensions_in_condition = 3;
repeated DurationBucketInfo bucket_info = 2;
}
@@ -84,7 +88,9 @@
}
message ValueMetricData {
- optional DimensionsValue dimension = 1;
+ optional DimensionsValue dimensions_in_what = 1;
+
+ optional DimensionsValue dimensions_in_condition = 3;
repeated ValueBucketInfo bucket_info = 2;
}
@@ -98,7 +104,9 @@
}
message GaugeMetricData {
- optional DimensionsValue dimension = 1;
+ optional DimensionsValue dimensions_in_what = 1;
+
+ optional DimensionsValue dimensions_in_condition = 3;
repeated GaugeBucketInfo bucket_info = 2;
}
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
index 09a43f5..cee9200 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -43,6 +43,19 @@
// Helper function to write PulledAtomStats to ProtoOutputStream
void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>& pair,
util::ProtoOutputStream* protoOutput);
+
+template<class T>
+bool parseProtoOutputStream(util::ProtoOutputStream& protoOutput, T* message) {
+ std::string pbBytes;
+ auto iter = protoOutput.data();
+ while (iter.readBuffer() != NULL) {
+ size_t toRead = iter.currentToRead();
+ pbBytes.append(reinterpret_cast<const char*>(iter.readBuffer()), toRead);
+ iter.rp()->move(toRead);
+ }
+ return message->ParseFromArray(pbBytes.c_str(), pbBytes.size());
+}
+
} // namespace statsd
} // namespace os
} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index d45a6b0..ae8a835 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -178,11 +178,13 @@
optional int64 condition = 3;
- optional FieldMatcher dimensions = 4;
+ optional FieldMatcher dimensions_in_what = 4;
- optional TimeUnit bucket = 5;
+ optional FieldMatcher dimensions_in_condition = 5;
- repeated MetricConditionLink links = 6;
+ optional TimeUnit bucket = 6;
+
+ repeated MetricConditionLink links = 7;
}
message DurationMetric {
@@ -201,9 +203,11 @@
}
optional AggregationType aggregation_type = 5 [default = SUM];
- optional FieldMatcher dimensions = 6;
+ optional FieldMatcher dimensions_in_what = 6;
- optional TimeUnit bucket = 7;
+ optional FieldMatcher dimensions_in_condition = 7;
+
+ optional TimeUnit bucket = 8;
}
message GaugeMetric {
@@ -215,11 +219,13 @@
optional int64 condition = 4;
- optional FieldMatcher dimensions = 5;
+ optional FieldMatcher dimensions_in_what = 5;
- optional TimeUnit bucket = 6;
+ optional FieldMatcher dimensions_in_condition = 6;
- repeated MetricConditionLink links = 7;
+ optional TimeUnit bucket = 7;
+
+ repeated MetricConditionLink links = 8;
}
message ValueMetric {
@@ -231,14 +237,16 @@
optional int64 condition = 4;
- optional FieldMatcher dimensions = 5;
+ optional FieldMatcher dimensions_in_what = 5;
- optional TimeUnit bucket = 6;
+ optional FieldMatcher dimensions_in_condition = 6;
- repeated MetricConditionLink links = 7;
+ optional TimeUnit bucket = 7;
+
+ repeated MetricConditionLink links = 8;
enum AggregationType { SUM = 1; }
- optional AggregationType aggregation_type = 8 [default = SUM];
+ optional AggregationType aggregation_type = 9 [default = SUM];
}
message Alert {
diff --git a/cmds/statsd/statsd.rc b/cmds/statsd/statsd.rc
index faccd61..c260ae1 100644
--- a/cmds/statsd/statsd.rc
+++ b/cmds/statsd/statsd.rc
@@ -14,3 +14,10 @@
service statsd /system/bin/statsd
class main
+ user statsd
+ group statsd log
+
+on post-fs-data
+ # Create directory for statsd
+ mkdir /data/misc/stats-data/ 0770 statsd statsd
+ mkdir /data/misc/stats-service/ 0770 statsd statsd
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index fe0f59d..f90ca40 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -81,8 +81,8 @@
metric->set_id(3);
metric->set_what(StringToId("SCREEN_IS_ON"));
metric->set_bucket(ONE_MINUTE);
- metric->mutable_dimensions()->set_field(2 /*SCREEN_STATE_CHANGE*/);
- metric->mutable_dimensions()->add_child()->set_field(1);
+ metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/);
+ metric->mutable_dimensions_in_what()->add_child()->set_field(1);
config.add_no_report_metric(3);
@@ -132,8 +132,8 @@
metric->set_id(3);
metric->set_what(StringToId("SCREEN_IS_ON"));
metric->set_bucket(ONE_MINUTE);
- metric->mutable_dimensions()->set_field(2 /*SCREEN_STATE_CHANGE*/);
- metric->mutable_dimensions()->add_child()->set_field(1);
+ metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/);
+ metric->mutable_dimensions_in_what()->add_child()->set_field(1);
auto alert = config.add_alert();
alert->set_id(3);
@@ -217,7 +217,7 @@
metric->set_what(StringToId("BATTERY_LOW"));
metric->set_bucket(ONE_MINUTE);
// This case is interesting. We want to dimension across two atoms.
- metric->mutable_dimensions()->add_child()->set_field(1);
+ metric->mutable_dimensions_in_what()->add_child()->set_field(1);
auto alert = config.add_alert();
alert->set_id(103);
diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
index 39c9549..a56db28 100644
--- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
@@ -44,7 +44,7 @@
auto countMetric = config.add_count_metric();
countMetric->set_id(123456);
countMetric->set_what(wakelockAcquireMatcher.id());
- *countMetric->mutable_dimensions() =
+ *countMetric->mutable_dimensions_in_what() =
CreateAttributionUidAndTagDimensions(
android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
countMetric->set_bucket(ONE_MINUTE);
@@ -155,7 +155,8 @@
auto data = countMetrics.data(0);
ValidateAttributionUidAndTagDimension(
- data.dimension(), android::util::WAKELOCK_STATE_CHANGED, 111, "App1");
+ data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 111,
+ "App1");
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).count(), 2);
EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
@@ -166,7 +167,8 @@
data = countMetrics.data(1);
ValidateAttributionUidAndTagDimension(
- data.dimension(), android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1");
+ data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 222,
+ "GMSCoreModule1");
EXPECT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).count(), 1);
EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
@@ -177,7 +179,8 @@
data = countMetrics.data(2);
ValidateAttributionUidAndTagDimension(
- data.dimension(), android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule3");
+ data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 222,
+ "GMSCoreModule3");
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).count(), 1);
EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + 3 * bucketSizeNs);
@@ -185,7 +188,8 @@
data = countMetrics.data(3);
ValidateAttributionUidAndTagDimension(
- data.dimension(), android::util::WAKELOCK_STATE_CHANGED, 444, "GMSCoreModule2");
+ data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 444,
+ "GMSCoreModule2");
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).count(), 1);
EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp
new file mode 100644
index 0000000..8250297
--- /dev/null
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp
@@ -0,0 +1,194 @@
+// Copyright (C) 2017 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 <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+namespace {
+
+StatsdConfig CreateStatsdConfigForPushedEvent() {
+ StatsdConfig config;
+ *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
+ *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
+
+ auto atomMatcher = CreateSimpleAtomMatcher("", android::util::APP_START_CHANGED);
+ *config.add_atom_matcher() = atomMatcher;
+
+ auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
+ *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
+ CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
+ *config.add_predicate() = isInBackgroundPredicate;
+
+ auto gaugeMetric = config.add_gauge_metric();
+ gaugeMetric->set_id(123456);
+ gaugeMetric->set_what(atomMatcher.id());
+ gaugeMetric->set_condition(isInBackgroundPredicate.id());
+ gaugeMetric->mutable_gauge_fields_filter()->set_include_all(false);
+ auto fieldMatcher = gaugeMetric->mutable_gauge_fields_filter()->mutable_fields();
+ fieldMatcher->set_field(android::util::APP_START_CHANGED);
+ fieldMatcher->add_child()->set_field(3); // type (enum)
+ fieldMatcher->add_child()->set_field(4); // activity_name(str)
+ fieldMatcher->add_child()->set_field(7); // activity_start_msec(int64)
+ *gaugeMetric->mutable_dimensions_in_what() =
+ CreateDimensions(android::util::APP_START_CHANGED, {1 /* uid field */ });
+ gaugeMetric->set_bucket(ONE_MINUTE);
+
+ auto links = gaugeMetric->add_links();
+ links->set_condition(isInBackgroundPredicate.id());
+ auto dimensionWhat = links->mutable_dimensions_in_what();
+ dimensionWhat->set_field(android::util::APP_START_CHANGED);
+ dimensionWhat->add_child()->set_field(1); // uid field.
+ auto dimensionCondition = links->mutable_dimensions_in_condition();
+ dimensionCondition->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+ dimensionCondition->add_child()->set_field(1); // uid field.
+ return config;
+}
+
+std::unique_ptr<LogEvent> CreateAppStartChangedEvent(
+ const int uid, const string& pkg_name, AppStartChanged::TransitionType type,
+ const string& activity_name, const string& calling_pkg_name, const bool is_instant_app,
+ int64_t activity_start_msec, uint64_t timestampNs) {
+ auto logEvent = std::make_unique<LogEvent>(
+ android::util::APP_START_CHANGED, timestampNs);
+ logEvent->write(uid);
+ logEvent->write(pkg_name);
+ logEvent->write(type);
+ logEvent->write(activity_name);
+ logEvent->write(calling_pkg_name);
+ logEvent->write(is_instant_app);
+ logEvent->write(activity_start_msec);
+ logEvent->init();
+ return logEvent;
+}
+
+} // namespace
+
+TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) {
+ auto config = CreateStatsdConfigForPushedEvent();
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+
+ ConfigKey cfgKey;
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+ int appUid1 = 123;
+ int appUid2 = 456;
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateMoveToBackgroundEvent(appUid1, bucketStartTimeNs + 15));
+ events.push_back(CreateMoveToForegroundEvent(appUid1, bucketStartTimeNs + bucketSizeNs + 250));
+ events.push_back(CreateMoveToBackgroundEvent(appUid1, bucketStartTimeNs + bucketSizeNs + 350));
+ events.push_back(CreateMoveToForegroundEvent(
+ appUid1, bucketStartTimeNs + 2 * bucketSizeNs + 100));
+
+
+ events.push_back(CreateAppStartChangedEvent(
+ appUid1, "app1", AppStartChanged::WARM, "activity_name1", "calling_pkg_name1",
+ true /*is_instant_app*/, 101 /*activity_start_msec*/, bucketStartTimeNs + 10));
+ events.push_back(CreateAppStartChangedEvent(
+ appUid1, "app1", AppStartChanged::HOT, "activity_name2", "calling_pkg_name2",
+ true /*is_instant_app*/, 102 /*activity_start_msec*/, bucketStartTimeNs + 20));
+ events.push_back(CreateAppStartChangedEvent(
+ appUid1, "app1", AppStartChanged::COLD, "activity_name3", "calling_pkg_name3",
+ true /*is_instant_app*/, 103 /*activity_start_msec*/, bucketStartTimeNs + 30));
+ events.push_back(CreateAppStartChangedEvent(
+ appUid1, "app1", AppStartChanged::WARM, "activity_name4", "calling_pkg_name4",
+ true /*is_instant_app*/, 104 /*activity_start_msec*/,
+ bucketStartTimeNs + bucketSizeNs + 30));
+ events.push_back(CreateAppStartChangedEvent(
+ appUid1, "app1", AppStartChanged::COLD, "activity_name5", "calling_pkg_name5",
+ true /*is_instant_app*/, 105 /*activity_start_msec*/,
+ bucketStartTimeNs + 2 * bucketSizeNs));
+ events.push_back(CreateAppStartChangedEvent(
+ appUid1, "app1", AppStartChanged::HOT, "activity_name6", "calling_pkg_name6",
+ false /*is_instant_app*/, 106 /*activity_start_msec*/,
+ bucketStartTimeNs + 2 * bucketSizeNs + 10));
+
+ events.push_back(CreateMoveToBackgroundEvent(appUid2, bucketStartTimeNs + bucketSizeNs + 10));
+ events.push_back(CreateAppStartChangedEvent(
+ appUid2, "app2", AppStartChanged::COLD, "activity_name7", "calling_pkg_name7",
+ true /*is_instant_app*/, 201 /*activity_start_msec*/,
+ bucketStartTimeNs + 2 * bucketSizeNs + 10));
+
+ sortLogEventsByTimestamp(&events);
+
+ for (const auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, &reports);
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+ EXPECT_EQ(gaugeMetrics.data_size(), 2);
+
+ auto data = gaugeMetrics.data(0);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::APP_START_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1 /* uid field */);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid1);
+ EXPECT_EQ(data.bucket_info_size(), 3);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(0).atom().app_start_changed().type(), AppStartChanged::HOT);
+ EXPECT_EQ(data.bucket_info(0).atom().app_start_changed().activity_name(), "activity_name2");
+ EXPECT_EQ(data.bucket_info(0).atom().app_start_changed().activity_start_msec(), 102L);
+
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).atom().app_start_changed().type(), AppStartChanged::WARM);
+ EXPECT_EQ(data.bucket_info(1).atom().app_start_changed().activity_name(), "activity_name4");
+ EXPECT_EQ(data.bucket_info(1).atom().app_start_changed().activity_start_msec(), 104L);
+
+ EXPECT_EQ(data.bucket_info(2).start_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(2).end_bucket_nanos(), bucketStartTimeNs + 3 * bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(2).atom().app_start_changed().type(), AppStartChanged::COLD);
+ EXPECT_EQ(data.bucket_info(2).atom().app_start_changed().activity_name(), "activity_name5");
+ EXPECT_EQ(data.bucket_info(2).atom().app_start_changed().activity_start_msec(), 105L);
+
+ data = gaugeMetrics.data(1);
+
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::APP_START_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1 /* uid field */);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid2);
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 3 * bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(0).atom().app_start_changed().type(), AppStartChanged::COLD);
+ EXPECT_EQ(data.bucket_info(0).atom().app_start_changed().activity_name(), "activity_name7");
+ EXPECT_EQ(data.bucket_info(0).atom().app_start_changed().activity_start_msec(), 201L);
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index cdc4467..178dd71 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -68,7 +68,7 @@
countMetric->set_what(appCrashMatcher.id());
countMetric->set_condition(combinationPredicate->id());
// The metric is dimensioning by uid only.
- *countMetric->mutable_dimensions() =
+ *countMetric->mutable_dimensions_in_what() =
CreateDimensions(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1});
countMetric->set_bucket(ONE_MINUTE);
@@ -199,12 +199,11 @@
EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1);
auto data = reports.reports(0).metrics(0).count_metrics().data(0);
// Validate dimension value.
- EXPECT_EQ(data.dimension().field(),
- android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
// Uid field.
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).value_int(), appUid);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid);
reports.Clear();
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
@@ -216,12 +215,12 @@
EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(1).count(), 3);
data = reports.reports(0).metrics(0).count_metrics().data(0);
// Validate dimension value.
- EXPECT_EQ(data.dimension().field(),
+ EXPECT_EQ(data.dimensions_in_what().field(),
android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
// Uid field.
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).value_int(), appUid);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid);
}
#else
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
index 2783356..e656b98 100644
--- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -26,6 +26,8 @@
#ifdef __ANDROID__
+namespace {
+
StatsdConfig CreateStatsdConfig(DurationMetric::AggregationType aggregationType) {
StatsdConfig config;
*config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
@@ -49,13 +51,15 @@
durationMetric->set_condition(screenIsOffPredicate.id());
durationMetric->set_aggregation_type(aggregationType);
// The metric is dimensioning by first attribution node and only by uid.
- *durationMetric->mutable_dimensions() =
+ *durationMetric->mutable_dimensions_in_what() =
CreateAttributionUidDimensions(
android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
durationMetric->set_bucket(ONE_MINUTE);
return config;
}
+} // namespace
+
TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions) {
ConfigKey cfgKey;
for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE }) {
@@ -94,6 +98,7 @@
auto releaseEvent2 = CreateReleaseWakelockEvent(
attributions2, "wl2", bucketStartTimeNs + 2 * bucketSizeNs - 15);
+
std::vector<std::unique_ptr<LogEvent>> events;
events.push_back(std::move(screenTurnedOnEvent));
@@ -119,18 +124,9 @@
auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
// Validate dimension value.
- EXPECT_EQ(data.dimension().field(),
- android::util::WAKELOCK_STATE_CHANGED);
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1);
- // Attribution field.
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1);
- // Uid only.
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value_size(), 1);
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value(0).value_int(), 111);
+ ValidateAttributionUidDimension(
+ data.dimensions_in_what(),
+ android::util::WAKELOCK_STATE_CHANGED, 111);
// Validate bucket info.
EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1);
data = reports.reports(0).metrics(0).duration_metrics().data(0);
@@ -147,18 +143,8 @@
EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2);
data = reports.reports(0).metrics(0).duration_metrics().data(0);
// Validate dimension value.
- EXPECT_EQ(data.dimension().field(),
- android::util::WAKELOCK_STATE_CHANGED);
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1);
- // Attribution field.
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1);
- // Uid only.
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value_size(), 1);
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value(0).value_int(), 111);
+ ValidateAttributionUidDimension(
+ data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 111);
// Two output buckets.
// The wakelock holding interval in the 1st bucket starts from the screen off event and to
// the end of the 1st bucket.
@@ -167,6 +153,32 @@
// The wakelock holding interval in the 2nd bucket starts at the beginning of the bucket and
// ends at the second screen on event.
EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 500UL);
+
+ events.clear();
+ events.push_back(CreateScreenStateChangedEvent(
+ ScreenStateChanged::STATE_OFF, bucketStartTimeNs + 2 * bucketSizeNs + 90));
+ events.push_back(CreateAcquireWakelockEvent(
+ attributions1, "wl3", bucketStartTimeNs + 2 * bucketSizeNs + 100));
+ events.push_back(CreateReleaseWakelockEvent(
+ attributions1, "wl3", bucketStartTimeNs + 5 * bucketSizeNs + 100));
+ sortLogEventsByTimestamp(&events);
+ for (const auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+ reports.Clear();
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, &reports);
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 6);
+ data = reports.reports(0).metrics(0).duration_metrics().data(0);
+ ValidateAttributionUidDimension(
+ data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 111);
+ // The last wakelock holding spans 4 buckets.
+ EXPECT_EQ((unsigned long long)data.bucket_info(2).duration_nanos(), bucketSizeNs - 100);
+ EXPECT_EQ((unsigned long long)data.bucket_info(3).duration_nanos(), bucketSizeNs);
+ EXPECT_EQ((unsigned long long)data.bucket_info(4).duration_nanos(), bucketSizeNs);
+ EXPECT_EQ((unsigned long long)data.bucket_info(5).duration_nanos(), 100UL);
}
}
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index e788235..718b2e1 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -19,6 +19,14 @@
namespace os {
namespace statsd {
+AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId) {
+ AtomMatcher atom_matcher;
+ atom_matcher.set_id(StringToId(name));
+ auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+ simple_atom_matcher->set_atom_id(atomId);
+ return atom_matcher;
+}
+
AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name,
WakelockStateChanged::State state) {
AtomMatcher atom_matcher;
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 1bbbd9a..1fc33de 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -23,6 +23,9 @@
namespace os {
namespace statsd {
+// Create AtomMatcher proto to simply match a specific atom type.
+AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId);
+
// Create AtomMatcher proto for acquiring wakelock.
AtomMatcher CreateAcquireWakelockAtomMatcher();
@@ -135,7 +138,7 @@
void sortMetricDataByDimensionsValue(const T& metricData, T* sortedMetricData) {
std::map<HashableDimensionKey, int> dimensionIndexMap;
for (int i = 0; i < metricData.data_size(); ++i) {
- dimensionIndexMap.insert(std::make_pair(metricData.data(i).dimension(), i));
+ dimensionIndexMap.insert(std::make_pair(metricData.data(i).dimensions_in_what(), i));
}
for (const auto& itr : dimensionIndexMap) {
*sortedMetricData->add_data() = metricData.data(itr.second);
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
index 93dba71..33c8abf 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
@@ -97,9 +97,14 @@
= log.getDurationMetrics();
sb.append("Dimension size: ").append(durationMetricDataWrapper.getDataCount()).append("\n");
for (StatsLog.DurationMetricData duration : durationMetricDataWrapper.getDataList()) {
- sb.append("dimension: ");
- displayDimension(sb, duration.getDimension());
+ sb.append("dimension_in_what: ");
+ displayDimension(sb, duration.getDimensionsInWhat());
sb.append("\n");
+ if (duration.hasDimensionsInCondition()) {
+ sb.append("dimension_in_condition: ");
+ displayDimension(sb, duration.getDimensionsInCondition());
+ sb.append("\n");
+ }
for (StatsLog.DurationBucketInfo info : duration.getBucketInfoList()) {
sb.append("\t[").append(getDateStr(info.getStartBucketNanos())).append("-")
@@ -124,9 +129,14 @@
= log.getCountMetrics();
sb.append("Dimension size: ").append(countMetricDataWrapper.getDataCount()).append("\n");
for (StatsLog.CountMetricData count : countMetricDataWrapper.getDataList()) {
- sb.append("dimension: ");
- displayDimension(sb, count.getDimension());
+ sb.append("dimension_in_what: ");
+ displayDimension(sb, count.getDimensionsInWhat());
sb.append("\n");
+ if (count.hasDimensionsInCondition()) {
+ sb.append("dimension_in_condition: ");
+ displayDimension(sb, count.getDimensionsInCondition());
+ sb.append("\n");
+ }
for (StatsLog.CountBucketInfo info : count.getBucketInfoList()) {
sb.append("\t[").append(getDateStr(info.getStartBucketNanos())).append("-")
diff --git a/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml b/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml
index 857853e..d6f8047 100644
--- a/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml
+++ b/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml
@@ -49,7 +49,7 @@
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:maxLength="3"
+ android:maxLength="4"
android:text="@integer/replication_default"
android:textSize="30dp"/>
</LinearLayout>
@@ -108,7 +108,7 @@
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:maxLength="2"
+ android:maxLength="4"
android:text="@integer/burst_default"
android:textSize="30dp"/>
</LinearLayout>
diff --git a/cmds/statsd/tools/loadtest/run_loadtest.sh b/cmds/statsd/tools/loadtest/run_loadtest.sh
new file mode 100755
index 0000000..3c93a06
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/run_loadtest.sh
@@ -0,0 +1,99 @@
+#!/bin/sh
+#
+# Script that measures statsd's PSS under an increasing number of metrics.
+
+# Globals.
+pss=""
+pid=""
+
+# Starts the loadtest.
+start_loadtest() {
+ echo "Starting loadtest"
+ adb shell am start -n com.android.statsd.loadtest/.LoadtestActivity --es "type" "start"
+}
+
+# Stops the loadtest.
+stop_loadtest() {
+ echo "Stopping loadtest"
+ adb shell am start -n com.android.statsd.loadtest/.LoadtestActivity --es "type" "stop"
+}
+
+# Sets the metrics replication.
+# Arguments:
+# $1: The replication factor.
+set_replication() {
+ adb shell am start -n com.android.statsd.loadtest/.LoadtestActivity --es "type" "set_replication" --ei "replication" "${1}"
+ echo "Replication set to ${1}"
+}
+
+# Reads statsd's pid and PSS.
+update_pid_and_pss() {
+ # Command that reads the PSS for statsd. This also gives us its pid.
+ get_mem=$(adb shell dumpsys meminfo |grep statsd)
+ # Looks for statsd's pid.
+ regex="([0-9,]+)K: statsd \(pid ([0-9]+)\).*"
+ if [[ $get_mem =~ $regex ]]; then
+ pss=$(echo "${BASH_REMATCH[1]}" | tr -d , | sed 's/\.//g')
+ pid=$(echo "${BASH_REMATCH[2]}")
+ else
+ echo $cmd doesnt match $regex
+ fi
+}
+
+# Kills statsd.
+# Assumes the pid has been set.
+kill_statsd() {
+ echo "Killing statsd (pid ${pid})"
+ adb shell kill -9 "${pid}"
+}
+
+# Main loop.
+main() {
+ start_time=$(date +%s)
+ values=()
+ stop_loadtest
+
+ echo ""
+ echo "********************* NEW LOADTEST ************************"
+ update_pid_and_pss
+ for replication in 1 2 4 8 16 32 64 128 256 512 1024 2048 4096
+ do
+ echo "**** Starting test at replication ${replication} ****"
+
+ # (1) Restart statsd. This will ensure its state is empty.
+ kill_statsd
+ sleep 3 # wait a bit for it to restart
+ update_pid_and_pss
+ echo "Before the test, statsd's PSS is ${pss}"
+
+ # (2) Set the replication.
+ set_replication "${replication}"
+ sleep 1 # wait a bit
+
+ # (3) Start the loadtest.
+ start_loadtest
+
+ # (4) Wait several seconds, then read the PSS.
+ sleep 100 && update_pid_and_pss
+ echo "During the test, statsd's PSS is ${pss}"
+ values+=(${pss})
+
+ echo "Values: ${values[@]}"
+
+ # (5) Stop loadtest.
+ stop_loadtest
+ sleep 2
+
+ echo ""
+ done
+
+ end_time=$(date +%s)
+ echo "Completed loadtest in $((${end_time} - ${start_time})) seconds."
+
+ values_as_str=$(IFS=$'\n'; echo "${values[*]}")
+ echo "The PSS values are:"
+ echo "${values_as_str}"
+ echo ""
+}
+
+main
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
index 19087d8..75489ad 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
@@ -22,18 +22,28 @@
import java.util.List;
public class DisplayProtoUtils {
+ private static final int MAX_NUM_METRICS_TO_DISPLAY = 10;
+
public static void displayLogReport(StringBuilder sb, StatsLog.ConfigMetricsReportList reports) {
- sb.append("ConfigKey: ");
+ sb.append("******************** Report ********************\n");
if (reports.hasConfigKey()) {
+ sb.append("ConfigKey: ");
com.android.os.StatsLog.ConfigMetricsReportList.ConfigKey key = reports.getConfigKey();
sb.append("\tuid: ").append(key.getUid()).append(" id: ").append(key.getId())
.append("\n");
}
+ int numMetrics = 0;
for (StatsLog.ConfigMetricsReport report : reports.getReportsList()) {
sb.append("StatsLogReport size: ").append(report.getMetricsCount()).append("\n");
for (StatsLog.StatsLogReport log : report.getMetricsList()) {
- sb.append("\n\n");
+ numMetrics++;
+ if (numMetrics > MAX_NUM_METRICS_TO_DISPLAY) {
+ sb.append("... output truncated\n");
+ sb.append("************************************************");
+ return;
+ }
+ sb.append("\n");
sb.append("metric id: ").append(log.getMetricId()).append("\n");
sb.append("start time:").append(getDateStr(log.getStartReportNanos())).append("\n");
sb.append("end time:").append(getDateStr(log.getEndReportNanos())).append("\n");
@@ -65,6 +75,7 @@
}
}
}
+ sb.append("************************************************");
}
public static String getDateStr(long nanoSec) {
@@ -97,9 +108,14 @@
= log.getDurationMetrics();
sb.append("Dimension size: ").append(durationMetricDataWrapper.getDataCount()).append("\n");
for (StatsLog.DurationMetricData duration : durationMetricDataWrapper.getDataList()) {
- sb.append("dimension: ");
- displayDimension(sb, duration.getDimension());
+ sb.append("dimension_in_what: ");
+ displayDimension(sb, duration.getDimensionsInWhat());
sb.append("\n");
+ if (duration.hasDimensionsInCondition()) {
+ sb.append("dimension_in_condition: ");
+ displayDimension(sb, duration.getDimensionsInCondition());
+ sb.append("\n");
+ }
for (StatsLog.DurationBucketInfo info : duration.getBucketInfoList()) {
sb.append("\t[").append(getDateStr(info.getStartBucketNanos())).append("-")
@@ -124,9 +140,14 @@
= log.getCountMetrics();
sb.append("Dimension size: ").append(countMetricDataWrapper.getDataCount()).append("\n");
for (StatsLog.CountMetricData count : countMetricDataWrapper.getDataList()) {
- sb.append("dimension: ");
- displayDimension(sb, count.getDimension());
+ sb.append("dimension_in_what: ");
+ displayDimension(sb, count.getDimensionsInWhat());
sb.append("\n");
+ if (count.hasDimensionsInCondition()) {
+ sb.append("dimension_in_condition: ");
+ displayDimension(sb, count.getDimensionsInCondition());
+ sb.append("\n");
+ }
for (StatsLog.CountBucketInfo info : count.getBucketInfoList()) {
sb.append("\t[").append(getDateStr(info.getStartBucketNanos())).append("-")
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
index c81ac07..26c1c72 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
@@ -76,10 +76,12 @@
*/
public class LoadtestActivity extends Activity implements AdapterView.OnItemSelectedListener {
- private static final String TAG = "StatsdLoadtest";
+ private static final String TAG = "loadtest.LoadtestActivity";
public static final String TYPE = "type";
private static final String PUSH_ALARM = "push_alarm";
public static final String PERF_ALARM = "perf_alarm";
+ private static final String SET_REPLICATION = "set_replication";
+ private static final String REPLICATION = "replication";
private static final String START = "start";
private static final String STOP = "stop";
private static final Map<String, TimeUnit> TIME_UNIT_MAP = initializeTimeUnitMap();
@@ -231,7 +233,7 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- Log.d(TAG, "Starting loadtest");
+ Log.d(TAG, "Starting loadtest Activity");
setContentView(R.layout.activity_loadtest);
mReportText = (TextView) findViewById(R.id.report_text);
@@ -289,6 +291,11 @@
case PUSH_ALARM:
onAlarm();
break;
+ case SET_REPLICATION:
+ if (intent.hasExtra(REPLICATION)) {
+ setReplication(intent.getIntExtra(REPLICATION, 0));
+ }
+ break;
case START:
startLoadtest();
break;
@@ -340,6 +347,10 @@
ConfigMetricsReportList reports = null;
try {
reports = ConfigMetricsReportList.parseFrom(data);
+ Log.d(TAG, "Num reports: " + reports.getReportsCount());
+ StringBuilder sb = new StringBuilder();
+ DisplayProtoUtils.displayLogReport(sb, reports);
+ Log.d(TAG, sb.toString());
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
Log.d(TAG, "Invalid data");
}
@@ -411,9 +422,6 @@
// Prepare to push a sequence of atoms to logd.
mPusher = new SequencePusher(mBurst, mPlacebo);
- // Force a data flush by requesting data.
- getData();
-
// Create a config and push it to statsd.
if (!setConfig(mFactory.getConfig(mReplication, mBucket, mPlacebo,
mIncludeCountMetric, mIncludeDurationMetric, mIncludeEventMetric,
@@ -464,6 +472,9 @@
mPerfData = null;
}
+ // Obtain the latest data and display it.
+ getData();
+
long elapsedTimeMins = (long) Math.floor(
(SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000);
mReportText.setText("Loadtest ended. Elapsed time = " + elapsedTimeMins + " min(s)");
@@ -541,7 +552,10 @@
}
private synchronized void setReplication(int replication) {
- mReplication = replication;
+ if (mStarted) {
+ return;
+ }
+ mReplicationText.setText("" + replication);
}
private synchronized void setPeriodSecs(long periodSecs) {
@@ -573,7 +587,7 @@
private void initBurst() {
mBurst = getResources().getInteger(R.integer.burst_default);
mBurstText = (EditText) findViewById(R.id.burst);
- mBurstText.addTextChangedListener(new NumericalWatcher(mBurstText, 0, 50) {
+ mBurstText.addTextChangedListener(new NumericalWatcher(mBurstText, 0, 1000) {
@Override
public void onNewValue(int newValue) {
setBurst(newValue);
@@ -585,10 +599,10 @@
private void initReplication() {
mReplication = getResources().getInteger(R.integer.replication_default);
mReplicationText = (EditText) findViewById(R.id.replication);
- mReplicationText.addTextChangedListener(new NumericalWatcher(mReplicationText, 1, 100) {
+ mReplicationText.addTextChangedListener(new NumericalWatcher(mReplicationText, 1, 4096) {
@Override
public void onNewValue(int newValue) {
- setReplication(newValue);
+ mReplication = newValue;
}
});
handleFocus(mReplicationText);
@@ -606,9 +620,7 @@
mBucketSpinner.setOnItemSelectedListener(this);
for (String label : TIME_UNIT_MAP.keySet()) {
- Log.d(TAG, "EVALUATE " + label + " VS " + defaultValue);
if (defaultValue.equals(TIME_UNIT_MAP.get(label).toString())) {
- Log.d(TAG, " FOUND IT");
mBucketSpinner.setSelection(dataAdapter.getPosition(label));
}
}
diff --git a/core/java/Android.bp b/core/java/Android.bp
index b43cf27..afa08e6 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -3,6 +3,11 @@
srcs: ["android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl"],
}
+filegroup {
+ name: "IDropBoxManagerService.aidl",
+ srcs: ["com/android/internal/os/IDropBoxManagerService.aidl"],
+}
+
// only used by key_store_service
cc_library_shared {
name: "libkeystore_aidl",
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index aa099eb..0a5b848 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -16,6 +16,7 @@
package android.app;
+import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static java.lang.Character.MIN_VALUE;
import android.annotation.CallSuper;
@@ -98,6 +99,7 @@
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
+import android.view.RemoteAnimationDefinition;
import android.view.SearchEvent;
import android.view.View;
import android.view.View.OnCreateContextMenuListener;
@@ -857,6 +859,7 @@
private boolean mHasCurrentPermissionsRequest;
private boolean mAutoFillResetNeeded;
+ private boolean mAutoFillIgnoreFirstResumePause;
/** The last autofill id that was returned from {@link #getNextAutofillId()} */
private int mLastAutofillId = View.LAST_APP_AUTOFILL_ID;
@@ -1253,10 +1256,7 @@
getApplication().dispatchActivityStarted(this);
if (mAutoFillResetNeeded) {
- AutofillManager afm = getAutofillManager();
- if (afm != null) {
- afm.onVisibleForAutofill();
- }
+ getAutofillManager().onVisibleForAutofill();
}
}
@@ -1320,6 +1320,20 @@
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
getApplication().dispatchActivityResumed(this);
mActivityTransitionState.onResume(this, isTopOfTask());
+ if (mAutoFillResetNeeded) {
+ if (!mAutoFillIgnoreFirstResumePause) {
+ View focus = getCurrentFocus();
+ if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
+ // TODO: in Activity killed/recreated case, i.e. SessionLifecycleTest#
+ // testDatasetVisibleWhileAutofilledAppIsLifecycled: the View's initial
+ // window visibility after recreation is INVISIBLE in onResume() and next frame
+ // ViewRootImpl.performTraversals() changes window visibility to VISIBLE.
+ // So we cannot call View.notifyEnterOrExited() which will do nothing
+ // when View.isVisibleToUser() is false.
+ getAutofillManager().notifyViewEntered(focus);
+ }
+ }
+ }
mCalled = true;
}
@@ -1681,6 +1695,19 @@
protected void onPause() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onPause " + this);
getApplication().dispatchActivityPaused(this);
+ if (mAutoFillResetNeeded) {
+ if (!mAutoFillIgnoreFirstResumePause) {
+ if (DEBUG_LIFECYCLE) Slog.v(TAG, "autofill notifyViewExited " + this);
+ View focus = getCurrentFocus();
+ if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
+ getAutofillManager().notifyViewExited(focus);
+ }
+ } else {
+ // reset after first pause()
+ if (DEBUG_LIFECYCLE) Slog.v(TAG, "autofill got first pause " + this);
+ mAutoFillIgnoreFirstResumePause = false;
+ }
+ }
mCalled = true;
}
@@ -1871,6 +1898,10 @@
mTranslucentCallback = null;
mCalled = true;
+ if (mAutoFillResetNeeded) {
+ getAutofillManager().onInvisibleForAutofill();
+ }
+
if (isFinishing()) {
if (mAutoFillResetNeeded) {
getAutofillManager().onActivityFinished();
@@ -6266,7 +6297,7 @@
mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix);
- final AutofillManager afm = getAutofillManager();
+ final AutofillManager afm = mAutofillManager;
if (afm != null) {
afm.dump(prefix, writer);
} else {
@@ -6616,7 +6647,6 @@
* to run as a {@link android.service.vr.VrListenerService} is not installed, or has
* not been enabled in user settings.
*
- * @see android.content.pm.PackageManager#FEATURE_VR_MODE
* @see android.content.pm.PackageManager#FEATURE_VR_MODE_HIGH_PERFORMANCE
* @see android.service.vr.VrListenerService
* @see android.provider.Settings#ACTION_VR_LISTENER_SETTINGS
@@ -7120,13 +7150,23 @@
}
}
- final void performResume() {
+ final void performResume(boolean followedByPause) {
performRestart(true /* start */);
mFragments.execPendingActions();
mLastNonConfigurationInstances = null;
+ if (mAutoFillResetNeeded) {
+ // When Activity is destroyed in paused state, and relaunch activity, there will be
+ // extra onResume and onPause event, ignore the first onResume and onPause.
+ // see ActivityThread.handleRelaunchActivity()
+ mAutoFillIgnoreFirstResumePause = followedByPause;
+ if (mAutoFillIgnoreFirstResumePause && DEBUG_LIFECYCLE) {
+ Slog.v(TAG, "autofill will ignore first pause when relaunching " + this);
+ }
+ }
+
mCalled = false;
// mResumed is set by the instrumentation
mInstrumentation.callActivityOnResume(this);
@@ -7311,7 +7351,7 @@
}
} else if (who.startsWith(AUTO_FILL_AUTH_WHO_PREFIX)) {
Intent resultData = (resultCode == Activity.RESULT_OK) ? data : null;
- getAutofillManager().onAuthenticationResult(requestCode, resultData);
+ getAutofillManager().onAuthenticationResult(requestCode, resultData, getCurrentFocus());
} else {
Fragment frag = mFragments.findFragmentByWho(who);
if (frag != null) {
@@ -7585,6 +7625,12 @@
return !mStopped;
}
+ /** @hide */
+ @Override
+ public boolean isDisablingEnterExitEventForAutofill() {
+ return mAutoFillIgnoreFirstResumePause || !mResumed;
+ }
+
/**
* If set to true, this indicates to the system that it should never take a
* screenshot of the activity to be used as a representation while it is not in a started state.
@@ -7659,6 +7705,22 @@
}
}
+ /**
+ * Registers remote animations per transition type for this activity.
+ *
+ * @param definition The remote animation definition that defines which transition whould run
+ * which remote animation.
+ * @hide
+ */
+ @RequiresPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS)
+ public void registerRemoteAnimations(RemoteAnimationDefinition definition) {
+ try {
+ ActivityManager.getService().registerRemoteAnimations(mToken, definition);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to call registerRemoteAnimations", e);
+ }
+ }
+
class HostCallbacks extends FragmentHostCallback<Activity> {
public HostCallbacks() {
super(Activity.this /*activity*/);
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 60a5a11..db12c37 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -319,4 +319,19 @@
}
public abstract void registerScreenObserver(ScreenObserver observer);
+
+ /**
+ * Returns if more users can be started without stopping currently running users.
+ */
+ public abstract boolean canStartMoreUsers();
+
+ /**
+ * Sets the user switcher message for switching from {@link android.os.UserHandle#SYSTEM}.
+ */
+ public abstract void setSwitchingFromSystemUserMessage(String switchingFromSystemUserMessage);
+
+ /**
+ * Sets the user switcher message for switching to {@link android.os.UserHandle#SYSTEM}.
+ */
+ public abstract void setSwitchingToSystemUserMessage(String switchingToSystemUserMessage);
}
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index e61c5b7..4bcd677 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -16,12 +16,14 @@
package android.app;
+import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.INVALID_DISPLAY;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.content.ComponentName;
import android.content.Context;
@@ -44,6 +46,7 @@
import android.util.Slog;
import android.view.AppTransitionAnimationSpec;
import android.view.IAppTransitionAnimationSpecsFuture;
+import android.view.RemoteAnimationAdapter;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
@@ -241,6 +244,8 @@
private static final String KEY_INSTANT_APP_VERIFICATION_BUNDLE
= "android:instantapps.installerbundle";
private static final String KEY_SPECS_FUTURE = "android:activity.specsFuture";
+ private static final String KEY_REMOTE_ANIMATION_ADAPTER
+ = "android:activity.remoteAnimationAdapter";
/** @hide */
public static final int ANIM_NONE = 0;
@@ -268,6 +273,8 @@
public static final int ANIM_CLIP_REVEAL = 11;
/** @hide */
public static final int ANIM_OPEN_CROSS_PROFILE_APPS = 12;
+ /** @hide */
+ public static final int ANIM_REMOTE_ANIMATION = 13;
private String mPackageName;
private Rect mLaunchBounds;
@@ -304,6 +311,7 @@
private int mRotationAnimationHint = -1;
private Bundle mAppVerificationBundle;
private IAppTransitionAnimationSpecsFuture mSpecsFuture;
+ private RemoteAnimationAdapter mRemoteAnimationAdapter;
/**
* Create an ActivityOptions specifying a custom animation to run when
@@ -826,6 +834,20 @@
return opts;
}
+ /**
+ * Create an {@link ActivityOptions} instance that lets the application control the entire
+ * animation using a {@link RemoteAnimationAdapter}.
+ * @hide
+ */
+ @RequiresPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS)
+ public static ActivityOptions makeRemoteAnimation(
+ RemoteAnimationAdapter remoteAnimationAdapter) {
+ final ActivityOptions opts = new ActivityOptions();
+ opts.mRemoteAnimationAdapter = remoteAnimationAdapter;
+ opts.mAnimationType = ANIM_REMOTE_ANIMATION;
+ return opts;
+ }
+
/** @hide */
public boolean getLaunchTaskBehind() {
return mAnimationType == ANIM_LAUNCH_TASK_BEHIND;
@@ -922,6 +944,7 @@
mSpecsFuture = IAppTransitionAnimationSpecsFuture.Stub.asInterface(opts.getBinder(
KEY_SPECS_FUTURE));
}
+ mRemoteAnimationAdapter = opts.getParcelable(KEY_REMOTE_ANIMATION_ADAPTER);
}
/**
@@ -1070,6 +1093,11 @@
}
/** @hide */
+ public RemoteAnimationAdapter getRemoteAnimationAdapter() {
+ return mRemoteAnimationAdapter;
+ }
+
+ /** @hide */
public static ActivityOptions fromBundle(Bundle bOptions) {
return bOptions != null ? new ActivityOptions(bOptions) : null;
}
@@ -1309,6 +1337,7 @@
mAnimSpecs = otherOptions.mAnimSpecs;
mAnimationFinishedListener = otherOptions.mAnimationFinishedListener;
mSpecsFuture = otherOptions.mSpecsFuture;
+ mRemoteAnimationAdapter = otherOptions.mRemoteAnimationAdapter;
}
/**
@@ -1403,7 +1432,9 @@
if (mAppVerificationBundle != null) {
b.putBundle(KEY_INSTANT_APP_VERIFICATION_BUNDLE, mAppVerificationBundle);
}
-
+ if (mRemoteAnimationAdapter != null) {
+ b.putParcelable(KEY_REMOTE_ANIMATION_ADAPTER, mRemoteAnimationAdapter);
+ }
return b;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index de346f3..934b0f3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -166,6 +166,7 @@
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.text.DateFormat;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -220,6 +221,9 @@
// Whether to invoke an activity callback after delivering new configuration.
private static final boolean REPORT_TO_ACTIVITY = true;
+ // Maximum number of recent tokens to maintain for debugging purposes
+ private static final int MAX_RECENT_TOKENS = 10;
+
/**
* Denotes an invalid sequence number corresponding to a process state change.
*/
@@ -252,6 +256,8 @@
final H mH = new H();
final Executor mExecutor = new HandlerExecutor(mH);
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
+ final ArrayDeque<Integer> mRecentTokens = new ArrayDeque<>();
+
// List of new activities (via ActivityRecord.nextIdle) that should
// be reported when next we idle.
ActivityClientRecord mNewActivities = null;
@@ -1752,9 +1758,11 @@
handleLocalVoiceInteractionStarted((IBinder) ((SomeArgs) msg.obj).arg1,
(IVoiceInteractor) ((SomeArgs) msg.obj).arg2);
break;
- case ATTACH_AGENT:
- handleAttachAgent((String) msg.obj);
+ case ATTACH_AGENT: {
+ Application app = getApplication();
+ handleAttachAgent((String) msg.obj, app != null ? app.mLoadedApk : null);
break;
+ }
case APPLICATION_INFO_CHANGED:
mUpdatingSystemConfig = true;
try {
@@ -1768,7 +1776,14 @@
(String[]) ((SomeArgs) msg.obj).arg2);
break;
case EXECUTE_TRANSACTION:
- mTransactionExecutor.execute(((ClientTransaction) msg.obj));
+ final ClientTransaction transaction = (ClientTransaction) msg.obj;
+ mTransactionExecutor.execute(transaction);
+ if (isSystem()) {
+ // Client transactions inside system process are recycled on the client side
+ // instead of ClientLifecycleManager to avoid being cleared before this
+ // message is handled.
+ transaction.recycle();
+ }
break;
}
Object obj = msg.obj;
@@ -2159,6 +2174,18 @@
pw.println(String.format(format, objs));
}
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "mActivities:");
+
+ for (ArrayMap.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
+ pw.println(prefix + " [token:" + entry.getKey().hashCode() + " record:"
+ + entry.getValue().toString() + "]");
+ }
+
+ pw.println(prefix + "mRecentTokens:" + mRecentTokens);
+ }
+
public static void dumpMemInfoTable(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin,
boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly,
int pid, String processName,
@@ -2843,6 +2870,11 @@
r.setState(ON_CREATE);
mActivities.put(r.token, r);
+ mRecentTokens.push(r.token.hashCode());
+
+ if (mRecentTokens.size() > MAX_RECENT_TOKENS) {
+ mRecentTokens.removeLast();
+ }
} catch (SuperNotCalledException e) {
throw e;
@@ -3064,7 +3096,7 @@
checkAndBlockForNetworkAccess();
deliverNewIntents(r, intents);
if (resumed) {
- r.activity.performResume();
+ r.activity.performResume(false);
r.activity.mTemporaryPause = false;
}
@@ -3239,11 +3271,23 @@
}
}
- static final void handleAttachAgent(String agent) {
+ private static boolean attemptAttachAgent(String agent, ClassLoader classLoader) {
try {
- VMDebug.attachAgent(agent);
+ VMDebug.attachAgent(agent, classLoader);
+ return true;
} catch (IOException e) {
- Slog.e(TAG, "Attaching agent failed: " + agent);
+ Slog.e(TAG, "Attaching agent with " + classLoader + " failed: " + agent);
+ return false;
+ }
+ }
+
+ static void handleAttachAgent(String agent, LoadedApk loadedApk) {
+ ClassLoader classLoader = loadedApk != null ? loadedApk.getClassLoader() : null;
+ if (attemptAttachAgent(agent, classLoader)) {
+ return;
+ }
+ if (classLoader != null) {
+ attemptAttachAgent(agent, null);
}
}
@@ -3674,7 +3718,7 @@
deliverResults(r, r.pendingResults);
r.pendingResults = null;
}
- r.activity.performResume();
+ r.activity.performResume(r.startsNotResumed);
synchronized (mResourcesManager) {
// If there is a pending local relaunch that was requested when the activity was
@@ -4393,7 +4437,7 @@
checkAndBlockForNetworkAccess();
deliverResults(r, results);
if (resumed) {
- r.activity.performResume();
+ r.activity.performResume(false);
r.activity.mTemporaryPause = false;
}
}
@@ -5535,12 +5579,16 @@
mCompatConfiguration = new Configuration(data.config);
mProfiler = new Profiler();
+ String agent = null;
if (data.initProfilerInfo != null) {
mProfiler.profileFile = data.initProfilerInfo.profileFile;
mProfiler.profileFd = data.initProfilerInfo.profileFd;
mProfiler.samplingInterval = data.initProfilerInfo.samplingInterval;
mProfiler.autoStopProfiler = data.initProfilerInfo.autoStopProfiler;
mProfiler.streamingOutput = data.initProfilerInfo.streamingOutput;
+ if (data.initProfilerInfo.attachAgentDuringBind) {
+ agent = data.initProfilerInfo.agent;
+ }
}
// send up app name; do this *before* waiting for debugger
@@ -5590,6 +5638,10 @@
data.loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo);
+ if (agent != null) {
+ handleAttachAgent(agent, data.loadedApk);
+ }
+
/**
* Switch this process to density compatibility mode if needed.
*/
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 82f2bac..4048e65 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2738,6 +2738,24 @@
}
@Override
+ public CharSequence getHarmfulAppWarning(String packageName) {
+ try {
+ return mPM.getHarmfulAppWarning(packageName, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void setHarmfulAppWarning(String packageName, CharSequence warning) {
+ try {
+ mPM.setHarmfulAppWarning(packageName, warning, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
public ArtManager getArtManager() {
synchronized (mLock) {
if (mArtManager == null) {
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 45c0e0c..0f66652 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -24,6 +24,7 @@
import com.android.internal.content.ReferrerIntent;
+import java.io.PrintWriter;
import java.util.List;
/**
@@ -121,4 +122,11 @@
* provided token.
*/
public abstract ActivityThread.ActivityClientRecord getActivityClient(IBinder token);
+
+ /**
+ * Debugging output.
+ * @param pw {@link PrintWriter} to write logs to.
+ * @param prefix Prefix to prepend to output.
+ */
+ public abstract void dump(PrintWriter pw, String prefix);
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index a9e633f..04ee77d 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -65,6 +65,7 @@
import android.os.StrictMode;
import android.os.WorkSource;
import android.service.voice.IVoiceInteractionSession;
+import android.view.RemoteAnimationDefinition;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.IResultReceiver;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -620,8 +621,9 @@
boolean updateDisplayOverrideConfiguration(in Configuration values, int displayId);
void moveStackToDisplay(int stackId, int displayId);
boolean requestAutofillData(in IAssistDataReceiver receiver, in Bundle receiverExtras,
- in IBinder activityToken, int flags);
- void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback);
+ in IBinder activityToken, int flags);
+ void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback,
+ in CharSequence message);
int restartUserInBackground(int userId);
/** Cancels the window transitions for the given task. */
@@ -671,4 +673,9 @@
* user unlock progress.
*/
boolean startUserInBackgroundWithListener(int userid, IProgressListener unlockProgressListener);
+
+ /**
+ * Registers remote animations for a specific activity.
+ */
+ void registerRemoteAnimations(in IBinder token, in RemoteAnimationDefinition definition);
}
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index d0f84c8..024dbcb 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.trust.ITrustManager;
import android.content.Context;
@@ -172,6 +173,7 @@
*
* @hide
*/
+ @SystemApi
public Intent createConfirmFactoryResetCredentialIntent(
CharSequence title, CharSequence description, CharSequence alternateButtonLabel) {
if (!LockPatternUtils.frpCredentialEnabled(mContext)) {
@@ -475,6 +477,39 @@
*/
public void requestDismissKeyguard(@NonNull Activity activity,
@Nullable KeyguardDismissCallback callback) {
+ requestDismissKeyguard(activity, null /* message */, callback);
+ }
+
+ /**
+ * If the device is currently locked (see {@link #isKeyguardLocked()}, requests the Keyguard to
+ * be dismissed.
+ * <p>
+ * If the Keyguard is not secure or the device is currently in a trusted state, calling this
+ * method will immediately dismiss the Keyguard without any user interaction.
+ * <p>
+ * If the Keyguard is secure and the device is not in a trusted state, this will bring up the
+ * UI so the user can enter their credentials.
+ * <p>
+ * If the value set for the {@link Activity} attr {@link android.R.attr#turnScreenOn} is true,
+ * the screen will turn on when the keyguard is dismissed.
+ *
+ * @param activity The activity requesting the dismissal. The activity must be either visible
+ * by using {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} or must be in a state in
+ * which it would be visible if Keyguard would not be hiding it. If that's not
+ * the case, the request will fail immediately and
+ * {@link KeyguardDismissCallback#onDismissError} will be invoked.
+ * @param message A message that will be shown in the keyguard explaining why the user
+ * would want to dismiss it.
+ * @param callback The callback to be called if the request to dismiss Keyguard was successful
+ * or {@code null} if the caller isn't interested in knowing the result. The
+ * callback will not be invoked if the activity was destroyed before the
+ * callback was received.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.SHOW_KEYGUARD_MESSAGE)
+ @SystemApi
+ public void requestDismissKeyguard(@NonNull Activity activity, @Nullable CharSequence message,
+ @Nullable KeyguardDismissCallback callback) {
try {
mAm.dismissKeyguard(activity.getActivityToken(), new IKeyguardDismissCallback.Stub() {
@Override
@@ -497,9 +532,9 @@
activity.mHandler.post(callback::onDismissCancelled);
}
}
- });
+ }, message);
} catch (RemoteException e) {
- Log.i(TAG, "Failed to dismiss keyguard: " + e);
+ throw e.rethrowFromSystemServer();
}
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 046a128..75dc571 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1022,10 +1022,18 @@
/**
* {@link #extras} key: A String array containing the people that this notification relates to,
* each of which was supplied to {@link Builder#addPerson(String)}.
+ *
+ * @deprecated the actual objects are now in {@link #EXTRA_PEOPLE_LIST}
*/
public static final String EXTRA_PEOPLE = "android.people";
/**
+ * {@link #extras} key: An arrayList of {@link Person} objects containing the people that
+ * this notification relates to.
+ */
+ public static final String EXTRA_PEOPLE_LIST = "android.people.list";
+
+ /**
* Allow certain system-generated notifications to appear before the device is provisioned.
* Only available to notifications coming from the android package.
* @hide
@@ -1063,10 +1071,20 @@
* direct replies
* {@link android.app.Notification.MessagingStyle} notification. This extra is a
* {@link CharSequence}
+ *
+ * @deprecated use {@link #EXTRA_MESSAGING_PERSON}
*/
public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
/**
+ * {@link #extras} key: the person to be displayed for all messages sent by the user including
+ * direct replies
+ * {@link android.app.Notification.MessagingStyle} notification. This extra is a
+ * {@link Person}
+ */
+ public static final String EXTRA_MESSAGING_PERSON = "android.messagingUser";
+
+ /**
* {@link #extras} key: a {@link CharSequence} to be displayed as the title to a conversation
* represented by a {@link android.app.Notification.MessagingStyle}
*/
@@ -1250,10 +1268,67 @@
*/
private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS";
+ /**
+ * {@link }: No semantic action defined.
+ */
+ public static final int SEMANTIC_ACTION_NONE = 0;
+
+ /**
+ * {@code SemanticAction}: Reply to a conversation, chat, group, or wherever replies
+ * may be appropriate.
+ */
+ public static final int SEMANTIC_ACTION_REPLY = 1;
+
+ /**
+ * {@code SemanticAction}: Mark content as read.
+ */
+ public static final int SEMANTIC_ACTION_MARK_AS_READ = 2;
+
+ /**
+ * {@code SemanticAction}: Mark content as unread.
+ */
+ public static final int SEMANTIC_ACTION_MARK_AS_UNREAD = 3;
+
+ /**
+ * {@code SemanticAction}: Delete the content associated with the notification. This
+ * could mean deleting an email, message, etc.
+ */
+ public static final int SEMANTIC_ACTION_DELETE = 4;
+
+ /**
+ * {@code SemanticAction}: Archive the content associated with the notification. This
+ * could mean archiving an email, message, etc.
+ */
+ public static final int SEMANTIC_ACTION_ARCHIVE = 5;
+
+ /**
+ * {@code SemanticAction}: Mute the content associated with the notification. This could
+ * mean silencing a conversation or currently playing media.
+ */
+ public static final int SEMANTIC_ACTION_MUTE = 6;
+
+ /**
+ * {@code SemanticAction}: Unmute the content associated with the notification. This could
+ * mean un-silencing a conversation or currently playing media.
+ */
+ public static final int SEMANTIC_ACTION_UNMUTE = 7;
+
+ /**
+ * {@code SemanticAction}: Mark content with a thumbs up.
+ */
+ public static final int SEMANTIC_ACTION_THUMBS_UP = 8;
+
+ /**
+ * {@code SemanticAction}: Mark content with a thumbs down.
+ */
+ public static final int SEMANTIC_ACTION_THUMBS_DOWN = 9;
+
+
private final Bundle mExtras;
private Icon mIcon;
private final RemoteInput[] mRemoteInputs;
private boolean mAllowGeneratedReplies = true;
+ private final @SemanticAction int mSemanticAction;
/**
* Small icon representing the action.
@@ -1288,6 +1363,7 @@
mExtras = Bundle.setDefusable(in.readBundle(), true);
mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
mAllowGeneratedReplies = in.readInt() == 1;
+ mSemanticAction = in.readInt();
}
/**
@@ -1295,12 +1371,14 @@
*/
@Deprecated
public Action(int icon, CharSequence title, PendingIntent intent) {
- this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true);
+ this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true,
+ SEMANTIC_ACTION_NONE);
}
/** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */
private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
- RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
+ RemoteInput[] remoteInputs, boolean allowGeneratedReplies,
+ @SemanticAction int semanticAction) {
this.mIcon = icon;
if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
this.icon = icon.getResId();
@@ -1310,6 +1388,7 @@
this.mExtras = extras != null ? extras : new Bundle();
this.mRemoteInputs = remoteInputs;
this.mAllowGeneratedReplies = allowGeneratedReplies;
+ this.mSemanticAction = semanticAction;
}
/**
@@ -1348,6 +1427,15 @@
}
/**
+ * Returns the {@code SemanticAction} associated with this {@link Action}. A
+ * {@code SemanticAction} denotes what an {@link Action}'s {@link PendingIntent} will do
+ * (eg. reply, mark as read, delete, etc).
+ */
+ public @SemanticAction int getSemanticAction() {
+ return mSemanticAction;
+ }
+
+ /**
* Get the list of inputs to be collected from the user that ONLY accept data when this
* action is sent. These remote inputs are guaranteed to return true on a call to
* {@link RemoteInput#isDataOnly}.
@@ -1371,6 +1459,7 @@
private boolean mAllowGeneratedReplies = true;
private final Bundle mExtras;
private ArrayList<RemoteInput> mRemoteInputs;
+ private @SemanticAction int mSemanticAction;
/**
* Construct a new builder for {@link Action} object.
@@ -1390,7 +1479,7 @@
* @param intent the {@link PendingIntent} to fire when users trigger this action
*/
public Builder(Icon icon, CharSequence title, PendingIntent intent) {
- this(icon, title, intent, new Bundle(), null, true);
+ this(icon, title, intent, new Bundle(), null, true, SEMANTIC_ACTION_NONE);
}
/**
@@ -1401,11 +1490,12 @@
public Builder(Action action) {
this(action.getIcon(), action.title, action.actionIntent,
new Bundle(action.mExtras), action.getRemoteInputs(),
- action.getAllowGeneratedReplies());
+ action.getAllowGeneratedReplies(), action.getSemanticAction());
}
private Builder(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
- RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
+ RemoteInput[] remoteInputs, boolean allowGeneratedReplies,
+ @SemanticAction int semanticAction) {
mIcon = icon;
mTitle = title;
mIntent = intent;
@@ -1415,6 +1505,7 @@
Collections.addAll(mRemoteInputs, remoteInputs);
}
mAllowGeneratedReplies = allowGeneratedReplies;
+ mSemanticAction = semanticAction;
}
/**
@@ -1470,6 +1561,19 @@
}
/**
+ * Sets the {@code SemanticAction} for this {@link Action}. A
+ * {@code SemanticAction} denotes what an {@link Action}'s
+ * {@link PendingIntent} will do (eg. reply, mark as read, delete, etc).
+ * @param semanticAction a SemanticAction defined within {@link Action} with
+ * {@code SEMANTIC_ACTION_} prefixes
+ * @return this object for method chaining
+ */
+ public Builder setSemanticAction(@SemanticAction int semanticAction) {
+ mSemanticAction = semanticAction;
+ return this;
+ }
+
+ /**
* Apply an extender to this action builder. Extenders may be used to add
* metadata or change options on this builder.
*/
@@ -1510,7 +1614,7 @@
RemoteInput[] textInputsArr = textInputs.isEmpty()
? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
- mAllowGeneratedReplies);
+ mAllowGeneratedReplies, mSemanticAction);
}
}
@@ -1522,12 +1626,15 @@
actionIntent, // safe to alias
mExtras == null ? new Bundle() : new Bundle(mExtras),
getRemoteInputs(),
- getAllowGeneratedReplies());
+ getAllowGeneratedReplies(),
+ getSemanticAction());
}
+
@Override
public int describeContents() {
return 0;
}
+
@Override
public void writeToParcel(Parcel out, int flags) {
final Icon ic = getIcon();
@@ -1547,7 +1654,9 @@
out.writeBundle(mExtras);
out.writeTypedArray(mRemoteInputs, flags);
out.writeInt(mAllowGeneratedReplies ? 1 : 0);
+ out.writeInt(mSemanticAction);
}
+
public static final Parcelable.Creator<Action> CREATOR =
new Parcelable.Creator<Action>() {
public Action createFromParcel(Parcel in) {
@@ -1809,6 +1918,29 @@
return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0;
}
}
+
+ /**
+ * Provides meaning to an {@link Action} that hints at what the associated
+ * {@link PendingIntent} will do. For example, an {@link Action} with a
+ * {@link PendingIntent} that replies to a text message notification may have the
+ * {@link #SEMANTIC_ACTION_REPLY} {@code SemanticAction} set within it.
+ *
+ * @hide
+ */
+ @IntDef(prefix = { "SEMANTIC_ACTION_" }, value = {
+ SEMANTIC_ACTION_NONE,
+ SEMANTIC_ACTION_REPLY,
+ SEMANTIC_ACTION_MARK_AS_READ,
+ SEMANTIC_ACTION_MARK_AS_UNREAD,
+ SEMANTIC_ACTION_DELETE,
+ SEMANTIC_ACTION_ARCHIVE,
+ SEMANTIC_ACTION_MUTE,
+ SEMANTIC_ACTION_UNMUTE,
+ SEMANTIC_ACTION_THUMBS_UP,
+ SEMANTIC_ACTION_THUMBS_DOWN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SemanticAction {}
}
/**
@@ -2819,7 +2951,7 @@
private Bundle mUserExtras = new Bundle();
private Style mStyle;
private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
- private ArrayList<String> mPersonList = new ArrayList<String>();
+ private ArrayList<Person> mPersonList = new ArrayList<>();
private NotificationColorUtil mColorUtil;
private boolean mIsLegacy;
private boolean mIsLegacyInitialized;
@@ -2910,8 +3042,9 @@
Collections.addAll(mActions, mN.actions);
}
- if (mN.extras.containsKey(EXTRA_PEOPLE)) {
- Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
+ if (mN.extras.containsKey(EXTRA_PEOPLE_LIST)) {
+ ArrayList<Person> people = mN.extras.getParcelableArrayList(EXTRA_PEOPLE_LIST);
+ mPersonList.addAll(people);
}
if (mN.getSmallIcon() == null && mN.icon != 0) {
@@ -3621,13 +3754,41 @@
* URIs. The path part of these URIs must exist in the contacts database, in the
* appropriate column, or the reference will be discarded as invalid. Telephone schema
* URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
+ * It is also possible to provide a URI with the schema {@code name:} in order to uniquely
+ * identify a person without an entry in the contacts database.
* </P>
*
* @param uri A URI for the person.
* @see Notification#EXTRA_PEOPLE
+ * @deprecated use {@link #addPerson(Person)}
*/
public Builder addPerson(String uri) {
- mPersonList.add(uri);
+ addPerson(new Person().setUri(uri));
+ return this;
+ }
+
+ /**
+ * Add a person that is relevant to this notification.
+ *
+ * <P>
+ * Depending on user preferences, this annotation may allow the notification to pass
+ * through interruption filters, if this notification is of category {@link #CATEGORY_CALL}
+ * or {@link #CATEGORY_MESSAGE}. The addition of people may also cause this notification to
+ * appear more prominently in the user interface.
+ * </P>
+ *
+ * <P>
+ * A person should usually contain a uri in order to benefit from the ranking boost.
+ * However, even if no uri is provided, it's beneficial to provide other people in the
+ * notification, such that listeners and voice only devices can announce and handle them
+ * properly.
+ * </P>
+ *
+ * @param person the person to add.
+ * @see Notification#EXTRA_PEOPLE_LIST
+ */
+ public Builder addPerson(Person person) {
+ mPersonList.add(person);
return this;
}
@@ -3934,7 +4095,10 @@
contentView.setViewVisibility(R.id.chronometer, View.GONE);
contentView.setViewVisibility(R.id.header_text, View.GONE);
contentView.setTextViewText(R.id.header_text, null);
+ contentView.setViewVisibility(R.id.header_text_secondary, View.GONE);
+ contentView.setTextViewText(R.id.header_text_secondary, null);
contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
+ contentView.setViewVisibility(R.id.header_text_secondary_divider, View.GONE);
contentView.setViewVisibility(R.id.time_divider, View.GONE);
contentView.setViewVisibility(R.id.time, View.GONE);
contentView.setImageViewIcon(R.id.profile_badge, null);
@@ -3965,7 +4129,7 @@
final Bundle ex = mN.extras;
updateBackgroundColor(contentView);
- bindNotificationHeader(contentView, p.ambient);
+ bindNotificationHeader(contentView, p.ambient, p.headerTextSecondary);
bindLargeIcon(contentView, p.hideLargeIcon, p.alwaysShowReply);
boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
if (p.title != null) {
@@ -4248,12 +4412,14 @@
return null;
}
- private void bindNotificationHeader(RemoteViews contentView, boolean ambient) {
+ private void bindNotificationHeader(RemoteViews contentView, boolean ambient,
+ CharSequence secondaryHeaderText) {
bindSmallIcon(contentView, ambient);
bindHeaderAppName(contentView, ambient);
if (!ambient) {
// Ambient view does not have these
bindHeaderText(contentView);
+ bindHeaderTextSecondary(contentView, secondaryHeaderText);
bindHeaderChronometerAndTime(contentView);
bindProfileBadge(contentView);
}
@@ -4322,6 +4488,17 @@
}
}
+ private void bindHeaderTextSecondary(RemoteViews contentView, CharSequence secondaryText) {
+ if (!TextUtils.isEmpty(secondaryText)) {
+ contentView.setTextViewText(R.id.header_text_secondary, processTextSpans(
+ processLegacyText(secondaryText)));
+ setTextViewColorSecondary(contentView, R.id.header_text_secondary);
+ contentView.setViewVisibility(R.id.header_text_secondary, View.VISIBLE);
+ contentView.setViewVisibility(R.id.header_text_secondary_divider, View.VISIBLE);
+ setTextViewColorSecondary(contentView, R.id.header_text_secondary_divider);
+ }
+ }
+
/**
* @hide
*/
@@ -4555,7 +4732,7 @@
ambient ? R.layout.notification_template_ambient_header
: R.layout.notification_template_header);
resetNotificationHeader(header);
- bindNotificationHeader(header, ambient);
+ bindNotificationHeader(header, ambient, null);
if (colorized != null) {
mN.extras.putBoolean(EXTRA_COLORIZED, colorized);
} else {
@@ -4968,8 +5145,7 @@
mActions.toArray(mN.actions);
}
if (!mPersonList.isEmpty()) {
- mN.extras.putStringArray(EXTRA_PEOPLE,
- mPersonList.toArray(new String[mPersonList.size()]));
+ mN.extras.putParcelableArrayList(EXTRA_PEOPLE_LIST, mPersonList);
}
if (mN.bigContentView != null || mN.contentView != null
|| mN.headsUpContentView != null) {
@@ -5965,7 +6141,7 @@
*/
public static final int MAXIMUM_RETAINED_MESSAGES = 25;
- CharSequence mUserDisplayName;
+ @NonNull Person mUser;
@Nullable CharSequence mConversationTitle;
List<Message> mMessages = new ArrayList<>();
List<Message> mHistoricMessages = new ArrayList<>();
@@ -5979,16 +6155,40 @@
* user before the posting app reposts the notification with those messages after they've
* been actually sent and in previous messages sent by the user added in
* {@link #addMessage(Notification.MessagingStyle.Message)}
+ *
+ * @deprecated use {@code MessagingStyle(Person)}
*/
public MessagingStyle(@NonNull CharSequence userDisplayName) {
- mUserDisplayName = userDisplayName;
+ this(new Person().setName(userDisplayName));
+ }
+
+ /**
+ * @param user Required - The person displayed for any messages that are sent by the
+ * user. Any messages added with {@link #addMessage(Notification.MessagingStyle.Message)}
+ * who don't have a Person associated with it will be displayed as if they were sent
+ * by this user. The user also needs to have a valid name associated with it.
+ */
+ public MessagingStyle(@NonNull Person user) {
+ mUser = user;
+ if (user == null || user.getName() == null) {
+ throw new RuntimeException("user must be valid and have a name");
+ }
+ }
+
+ /**
+ * @return the user to be displayed for any replies sent by the user
+ */
+ public Person getUser() {
+ return mUser;
}
/**
* Returns the name to be displayed for any replies sent by the user
+ *
+ * @deprecated use {@link #getUser()} instead
*/
public CharSequence getUserDisplayName() {
- return mUserDisplayName;
+ return mUser.getName();
}
/**
@@ -6031,8 +6231,28 @@
* @see Message#Message(CharSequence, long, CharSequence)
*
* @return this object for method chaining
+ *
+ * @deprecated use {@link #addMessage(CharSequence, long, Person)}
*/
public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
+ return addMessage(text, timestamp,
+ sender == null ? null : new Person().setName(sender));
+ }
+
+ /**
+ * Adds a message for display by this notification. Convenience call for a simple
+ * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
+ * @param text A {@link CharSequence} to be displayed as the message content
+ * @param timestamp Time at which the message arrived
+ * @param sender The {@link Person} who sent the message.
+ * Should be <code>null</code> for messages by the current user, in which case
+ * the platform will insert the user set in {@code MessagingStyle(Person)}.
+ *
+ * @see Message#Message(CharSequence, long, CharSequence)
+ *
+ * @return this object for method chaining
+ */
+ public MessagingStyle addMessage(CharSequence text, long timestamp, Person sender) {
return addMessage(new Message(text, timestamp, sender));
}
@@ -6131,8 +6351,10 @@
@Override
public void addExtras(Bundle extras) {
super.addExtras(extras);
- if (mUserDisplayName != null) {
- extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
+ if (mUser != null) {
+ // For legacy usages
+ extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUser.getName());
+ extras.putParcelable(EXTRA_MESSAGING_PERSON, mUser);
}
if (mConversationTitle != null) {
extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
@@ -6152,14 +6374,15 @@
Message m = findLatestIncomingMessage();
CharSequence text = (m == null) ? null : m.mText;
CharSequence sender = m == null ? null
- : TextUtils.isEmpty(m.mSender) ? mUserDisplayName : m.mSender;
+ : m.mSender == null || TextUtils.isEmpty(m.mSender.getName())
+ ? mUser.getName() : m.mSender.getName();
CharSequence title;
if (!TextUtils.isEmpty(mConversationTitle)) {
if (!TextUtils.isEmpty(sender)) {
BidiFormatter bidi = BidiFormatter.getInstance();
title = mBuilder.mContext.getString(
com.android.internal.R.string.notification_messaging_title_template,
- bidi.unicodeWrap(mConversationTitle), bidi.unicodeWrap(m.mSender));
+ bidi.unicodeWrap(mConversationTitle), bidi.unicodeWrap(sender));
} else {
title = mConversationTitle;
}
@@ -6182,7 +6405,11 @@
protected void restoreFromExtras(Bundle extras) {
super.restoreFromExtras(extras);
- mUserDisplayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
+ mUser = extras.getParcelable(EXTRA_MESSAGING_PERSON);
+ if (mUser == null) {
+ CharSequence displayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
+ mUser = new Person().setName(displayName);
+ }
mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE);
Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
mMessages = Message.getMessagesFromBundleArray(messages);
@@ -6198,7 +6425,7 @@
public RemoteViews makeContentView(boolean increasedHeight) {
mBuilder.mOriginalActions = mBuilder.mActions;
mBuilder.mActions = new ArrayList<>();
- RemoteViews remoteViews = makeBigContentView();
+ RemoteViews remoteViews = makeBigContentView(true /* showRightIcon */);
mBuilder.mActions = mBuilder.mOriginalActions;
mBuilder.mOriginalActions = null;
return remoteViews;
@@ -6217,7 +6444,7 @@
for (int i = messages.size() - 1; i >= 0; i--) {
Message m = messages.get(i);
// Incoming messages have a non-empty sender.
- if (!TextUtils.isEmpty(m.mSender)) {
+ if (m.mSender != null && !TextUtils.isEmpty(m.mSender.getName())) {
return m;
}
}
@@ -6233,23 +6460,31 @@
*/
@Override
public RemoteViews makeBigContentView() {
+ return makeBigContentView(false /* showRightIcon */);
+ }
+
+ @NonNull
+ private RemoteViews makeBigContentView(boolean showRightIcon) {
CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle)
? super.mBigContentTitle
: mConversationTitle;
boolean isOneToOne = TextUtils.isEmpty(conversationTitle);
- if (isOneToOne) {
- // Let's add the conversationTitle in case we didn't have one before and all
- // messages are from the same sender
- conversationTitle = createConversationTitleFromMessages();
- } else if (hasOnlyWhiteSpaceSenders()) {
+ if (hasOnlyWhiteSpaceSenders()) {
isOneToOne = true;
}
- boolean hasTitle = !TextUtils.isEmpty(conversationTitle);
RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
mBuilder.getMessagingLayoutResource(),
mBuilder.mParams.reset().hasProgress(false).title(conversationTitle).text(null)
- .hideLargeIcon(isOneToOne).alwaysShowReply(true));
+ .hideLargeIcon(!showRightIcon || isOneToOne)
+ .headerTextSecondary(conversationTitle)
+ .alwaysShowReply(showRightIcon));
addExtras(mBuilder.mN.extras);
+ // also update the end margin if there is an image
+ int endMargin = R.dimen.notification_content_margin_end;
+ if (mBuilder.mN.hasLargeIcon() && showRightIcon) {
+ endMargin = R.dimen.notification_content_plus_picture_margin_end;
+ }
+ contentView.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
mBuilder.resolveContrastColor());
contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon",
@@ -6264,8 +6499,8 @@
private boolean hasOnlyWhiteSpaceSenders() {
for (int i = 0; i < mMessages.size(); i++) {
Message m = mMessages.get(i);
- CharSequence sender = m.getSender();
- if (!isWhiteSpace(sender)) {
+ Person sender = m.getSenderPerson();
+ if (sender != null && !isWhiteSpace(sender.getName())) {
return false;
}
}
@@ -6294,9 +6529,9 @@
ArraySet<CharSequence> names = new ArraySet<>();
for (int i = 0; i < mMessages.size(); i++) {
Message m = mMessages.get(i);
- CharSequence sender = m.getSender();
+ Person sender = m.getSenderPerson();
if (sender != null) {
- names.add(sender);
+ names.add(sender.getName());
}
}
SpannableStringBuilder title = new SpannableStringBuilder();
@@ -6316,7 +6551,7 @@
*/
@Override
public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
- RemoteViews remoteViews = makeBigContentView();
+ RemoteViews remoteViews = makeBigContentView(true /* showRightIcon */);
remoteViews.setInt(R.id.notification_messaging, "setMaxDisplayedLines", 1);
return remoteViews;
}
@@ -6331,13 +6566,15 @@
static final String KEY_TEXT = "text";
static final String KEY_TIMESTAMP = "time";
static final String KEY_SENDER = "sender";
+ static final String KEY_SENDER_PERSON = "sender_person";
static final String KEY_DATA_MIME_TYPE = "type";
static final String KEY_DATA_URI= "uri";
static final String KEY_EXTRAS_BUNDLE = "extras";
private final CharSequence mText;
private final long mTimestamp;
- private final CharSequence mSender;
+ @Nullable
+ private final Person mSender;
private Bundle mExtras = new Bundle();
private String mDataMimeType;
@@ -6352,8 +6589,28 @@
* the platform will insert {@link MessagingStyle#getUserDisplayName()}.
* Should be unique amongst all individuals in the conversation, and should be
* consistent during re-posts of the notification.
+ *
+ * @deprecated use {@code Message(CharSequence, long, Person)}
*/
public Message(CharSequence text, long timestamp, CharSequence sender){
+ this(text, timestamp, sender == null ? null : new Person().setName(sender));
+ }
+
+ /**
+ * Constructor
+ * @param text A {@link CharSequence} to be displayed as the message content
+ * @param timestamp Time at which the message arrived
+ * @param sender The {@link Person} who sent the message.
+ * Should be <code>null</code> for messages by the current user, in which case
+ * the platform will insert the user set in {@code MessagingStyle(Person)}.
+ * <p>
+ * The person provided should contain an Icon, set with {@link Person#setIcon(Icon)}
+ * and also have a name provided with {@link Person#setName(CharSequence)}. If multiple
+ * users have the same name, consider providing a key with {@link Person#setKey(String)}
+ * in order to differentiate between the different users.
+ * </p>
+ */
+ public Message(CharSequence text, long timestamp, @Nullable Person sender){
mText = text;
mTimestamp = timestamp;
mSender = sender;
@@ -6416,8 +6673,18 @@
/**
* Get the text used to display the contact's name in the messaging experience
+ *
+ * @deprecated use {@link #getSenderPerson()}
*/
public CharSequence getSender() {
+ return mSender == null ? null : mSender.getName();
+ }
+
+ /**
+ * Get the sender associated with this message.
+ */
+ @Nullable
+ public Person getSenderPerson() {
return mSender;
}
@@ -6443,7 +6710,9 @@
}
bundle.putLong(KEY_TIMESTAMP, mTimestamp);
if (mSender != null) {
- bundle.putCharSequence(KEY_SENDER, mSender);
+ // Legacy listeners need this
+ bundle.putCharSequence(KEY_SENDER, mSender.getName());
+ bundle.putParcelable(KEY_SENDER_PERSON, mSender);
}
if (mDataMimeType != null) {
bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
@@ -6492,8 +6761,20 @@
if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
return null;
} else {
+
+ Person senderPerson = bundle.getParcelable(KEY_SENDER_PERSON);
+ if (senderPerson == null) {
+ // Legacy apps that use compat don't actually provide the sender objects
+ // We need to fix the compat version to provide people / use
+ // the native api instead
+ CharSequence senderName = bundle.getCharSequence(KEY_SENDER);
+ if (senderName != null) {
+ senderPerson = new Person().setName(senderName);
+ }
+ }
Message message = new Message(bundle.getCharSequence(KEY_TEXT),
- bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
+ bundle.getLong(KEY_TIMESTAMP),
+ senderPerson);
if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
bundle.containsKey(KEY_DATA_URI)) {
message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
@@ -7128,6 +7409,176 @@
}
}
+ /**
+ * A Person associated with this Notification.
+ */
+ public static final class Person implements Parcelable {
+ @Nullable private CharSequence mName;
+ @Nullable private Icon mIcon;
+ @Nullable private String mUri;
+ @Nullable private String mKey;
+
+ protected Person(Parcel in) {
+ mName = in.readCharSequence();
+ if (in.readInt() != 0) {
+ mIcon = Icon.CREATOR.createFromParcel(in);
+ }
+ mUri = in.readString();
+ mKey = in.readString();
+ }
+
+ /**
+ * Create a new person.
+ */
+ public Person() {
+ }
+
+ /**
+ * Give this person a name.
+ *
+ * @param name the name of this person
+ */
+ public Person setName(@Nullable CharSequence name) {
+ this.mName = name;
+ return this;
+ }
+
+ /**
+ * Add an icon for this person.
+ * <br />
+ * This is currently only used for {@link MessagingStyle} notifications and should not be
+ * provided otherwise, in order to save memory. The system will prefer this icon over any
+ * images that are resolved from the URI.
+ *
+ * @param icon the icon of the person
+ */
+ public Person setIcon(@Nullable Icon icon) {
+ this.mIcon = icon;
+ return this;
+ }
+
+ /**
+ * Set a URI associated with this person.
+ *
+ * <P>
+ * Depending on user preferences, adding a URI to a Person may allow the notification to
+ * pass through interruption filters, if this notification is of
+ * category {@link #CATEGORY_CALL} or {@link #CATEGORY_MESSAGE}.
+ * The addition of people may also cause this notification to appear more prominently in
+ * the user interface.
+ * </P>
+ *
+ * <P>
+ * The person should be specified by the {@code String} representation of a
+ * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
+ * </P>
+ *
+ * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
+ * URIs. The path part of these URIs must exist in the contacts database, in the
+ * appropriate column, or the reference will be discarded as invalid. Telephone schema
+ * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
+ * </P>
+ *
+ * @param uri a URI for the person
+ */
+ public Person setUri(@Nullable String uri) {
+ mUri = uri;
+ return this;
+ }
+
+ /**
+ * Add a key to this person in order to uniquely identify it.
+ * This is especially useful if the name doesn't uniquely identify this person or if the
+ * display name is a short handle of the actual name.
+ *
+ * <P>If no key is provided, the name serves as as the key for the purpose of
+ * identification.</P>
+ *
+ * @param key the key that uniquely identifies this person
+ */
+ public Person setKey(@Nullable String key) {
+ mKey = key;
+ return this;
+ }
+
+
+ /**
+ * @return the uri provided for this person or {@code null} if no Uri was provided
+ */
+ @Nullable
+ public String getUri() {
+ return mUri;
+ }
+
+ /**
+ * @return the name provided for this person or {@code null} if no name was provided
+ */
+ @Nullable
+ public CharSequence getName() {
+ return mName;
+ }
+
+ /**
+ * @return the icon provided for this person or {@code null} if no icon was provided
+ */
+ @Nullable
+ public Icon getIcon() {
+ return mIcon;
+ }
+
+ /**
+ * @return the key provided for this person or {@code null} if no key was provided
+ */
+ @Nullable
+ public String getKey() {
+ return mKey;
+ }
+
+ /**
+ * @return the URI associated with this person, or "name:mName" otherwise
+ * @hide
+ */
+ public String resolveToLegacyUri() {
+ if (mUri != null) {
+ return mUri;
+ }
+ if (mName != null) {
+ return "name:" + mName;
+ }
+ return "";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, @WriteFlags int flags) {
+ dest.writeCharSequence(mName);
+ if (mIcon != null) {
+ dest.writeInt(1);
+ mIcon.writeToParcel(dest, 0);
+ } else {
+ dest.writeInt(0);
+ }
+ dest.writeString(mUri);
+ dest.writeString(mKey);
+ }
+
+ public static final Creator<Person> CREATOR = new Creator<Person>() {
+ @Override
+ public Person createFromParcel(Parcel in) {
+ return new Person(in);
+ }
+
+ @Override
+ public Person[] newArray(int size) {
+ return new Person[size];
+ }
+ };
+ }
+
// When adding a new Style subclass here, don't forget to update
// Builder.getNotificationStyleClass.
@@ -8567,6 +9018,7 @@
boolean ambient = false;
CharSequence title;
CharSequence text;
+ CharSequence headerTextSecondary;
boolean hideLargeIcon;
public boolean alwaysShowReply;
@@ -8575,6 +9027,7 @@
ambient = false;
title = null;
text = null;
+ headerTextSecondary = null;
return this;
}
@@ -8593,6 +9046,11 @@
return this;
}
+ final StandardTemplateParams headerTextSecondary(CharSequence text) {
+ this.headerTextSecondary = text;
+ return this;
+ }
+
final StandardTemplateParams alwaysShowReply(boolean alwaysShowReply) {
this.alwaysShowReply = alwaysShowReply;
return this;
diff --git a/core/java/android/app/ProfilerInfo.java b/core/java/android/app/ProfilerInfo.java
index d523427..a295c4c 100644
--- a/core/java/android/app/ProfilerInfo.java
+++ b/core/java/android/app/ProfilerInfo.java
@@ -55,14 +55,24 @@
*/
public final String agent;
+ /**
+ * Whether the {@link agent} should be attached early (before bind-application) or during
+ * bind-application. Agents attached prior to binding cannot be loaded from the app's APK
+ * directly and must be given as an absolute path (or available in the default LD_LIBRARY_PATH).
+ * Agents attached during bind-application will miss early setup (e.g., resource initialization
+ * and classloader generation), but are searched in the app's library search path.
+ */
+ public final boolean attachAgentDuringBind;
+
public ProfilerInfo(String filename, ParcelFileDescriptor fd, int interval, boolean autoStop,
- boolean streaming, String agent) {
+ boolean streaming, String agent, boolean attachAgentDuringBind) {
profileFile = filename;
profileFd = fd;
samplingInterval = interval;
autoStopProfiler = autoStop;
streamingOutput = streaming;
this.agent = agent;
+ this.attachAgentDuringBind = attachAgentDuringBind;
}
public ProfilerInfo(ProfilerInfo in) {
@@ -72,6 +82,7 @@
autoStopProfiler = in.autoStopProfiler;
streamingOutput = in.streamingOutput;
agent = in.agent;
+ attachAgentDuringBind = in.attachAgentDuringBind;
}
/**
@@ -110,6 +121,7 @@
out.writeInt(autoStopProfiler ? 1 : 0);
out.writeInt(streamingOutput ? 1 : 0);
out.writeString(agent);
+ out.writeBoolean(attachAgentDuringBind);
}
public static final Parcelable.Creator<ProfilerInfo> CREATOR =
@@ -132,6 +144,7 @@
autoStopProfiler = in.readInt() != 0;
streamingOutput = in.readInt() != 0;
agent = in.readString();
+ attachAgentDuringBind = in.readBoolean();
}
@Override
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index 8c47598..6ac15a5 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -71,6 +71,8 @@
@GuardedBy("mLock")
private Map<String, Object> mMap;
+ @GuardedBy("mLock")
+ private Throwable mThrowable;
@GuardedBy("mLock")
private int mDiskWritesInFlight = 0;
@@ -107,6 +109,7 @@
mMode = mode;
mLoaded = false;
mMap = null;
+ mThrowable = null;
startLoadFromDisk();
}
@@ -139,13 +142,14 @@
Map<String, Object> map = null;
StructStat stat = null;
+ Throwable thrown = null;
try {
stat = Os.stat(mFile.getPath());
if (mFile.canRead()) {
BufferedInputStream str = null;
try {
str = new BufferedInputStream(
- new FileInputStream(mFile), 16*1024);
+ new FileInputStream(mFile), 16 * 1024);
map = (Map<String, Object>) XmlUtils.readMapXml(str);
} catch (Exception e) {
Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e);
@@ -154,19 +158,36 @@
}
}
} catch (ErrnoException e) {
- /* ignore */
+ // An errno exception means the stat failed. Treat as empty/non-existing by
+ // ignoring.
+ } catch (Throwable t) {
+ thrown = t;
}
synchronized (mLock) {
mLoaded = true;
- if (map != null) {
- mMap = map;
- mStatTimestamp = stat.st_mtim;
- mStatSize = stat.st_size;
- } else {
- mMap = new HashMap<>();
+ mThrowable = thrown;
+
+ // It's important that we always signal waiters, even if we'll make
+ // them fail with an exception. The try-finally is pretty wide, but
+ // better safe than sorry.
+ try {
+ if (thrown == null) {
+ if (map != null) {
+ mMap = map;
+ mStatTimestamp = stat.st_mtim;
+ mStatSize = stat.st_size;
+ } else {
+ mMap = new HashMap<>();
+ }
+ }
+ // In case of a thrown exception, we retain the old map. That allows
+ // any open editors to commit and store updates.
+ } catch (Throwable t) {
+ mThrowable = t;
+ } finally {
+ mLock.notifyAll();
}
- mLock.notifyAll();
}
}
@@ -226,6 +247,7 @@
}
}
+ @GuardedBy("mLock")
private void awaitLoadedLocked() {
if (!mLoaded) {
// Raise an explicit StrictMode onReadFromDisk for this
@@ -239,6 +261,9 @@
} catch (InterruptedException unused) {
}
}
+ if (mThrowable != null) {
+ throw new IllegalStateException(mThrowable);
+ }
}
@Override
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 6eb4783..6eafcc4 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -136,6 +136,7 @@
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.euicc.EuiccCardManager;
import android.telephony.euicc.EuiccManager;
import android.util.Log;
import android.util.StatsManager;
@@ -519,6 +520,13 @@
return new EuiccManager(ctx.getOuterContext());
}});
+ registerService(Context.EUICC_CARD_SERVICE, EuiccCardManager.class,
+ new CachedServiceFetcher<EuiccCardManager>() {
+ @Override
+ public EuiccCardManager createService(ContextImpl ctx) {
+ return new EuiccCardManager(ctx.getOuterContext());
+ }});
+
registerService(Context.UI_MODE_SERVICE, UiModeManager.class,
new CachedServiceFetcher<UiModeManager>() {
@Override
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 8f01685..ba39740 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -24,7 +24,6 @@
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
@@ -47,10 +46,14 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
+
+import com.android.internal.util.CollectionUtils;
+
import libcore.io.IoUtils;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeoutException;
@@ -580,6 +583,8 @@
// Execute the command *without* the lock being held.
command.run();
+ List<AccessibilityEvent> eventsReceived = Collections.emptyList();
+
// Acquire the lock and wait for the event.
try {
// Wait for the event.
@@ -600,14 +605,14 @@
if (filter.accept(event)) {
return event;
}
- event.recycle();
+ eventsReceived = CollectionUtils.add(eventsReceived, event);
}
// Check if timed out and if not wait.
final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
if (remainingTimeMillis <= 0) {
throw new TimeoutException("Expected event not received within: "
- + timeoutMillis + " ms.");
+ + timeoutMillis + " ms, among " + eventsReceived);
}
synchronized (mLock) {
if (mEventQueue.isEmpty()) {
@@ -620,6 +625,10 @@
}
}
} finally {
+ for (int i = 0; i < CollectionUtils.size(eventsReceived); i++) {
+ AccessibilityEvent event = eventsReceived.get(i);
+ event.recycle();
+ }
synchronized (mLock) {
mWaitingForEventDelivery = false;
mEventQueue.clear();
diff --git a/core/java/android/app/admin/ConnectEvent.java b/core/java/android/app/admin/ConnectEvent.java
index f06a925..d511c57 100644
--- a/core/java/android/app/admin/ConnectEvent.java
+++ b/core/java/android/app/admin/ConnectEvent.java
@@ -68,7 +68,7 @@
@Override
public String toString() {
- return String.format("ConnectEvent(%s, %d, %d, %s)", mIpAddress, mPort, mTimestamp,
+ return String.format("ConnectEvent(%d, %s, %d, %d, %s)", mId, mIpAddress, mPort, mTimestamp,
mPackageName);
}
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index 2e697ac..302d52f 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -29,10 +29,14 @@
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserHandle;
import android.security.KeyChain;
+import libcore.util.NonNull;
+import libcore.util.Nullable;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -438,6 +442,56 @@
// TO DO: describe syntax.
public static final String DEVICE_ADMIN_META_DATA = "android.app.device_admin";
+ /**
+ * Broadcast action: notify the newly transferred administrator that the transfer
+ * from the original administrator was successful.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_TRANSFER_OWNERSHIP_COMPLETE =
+ "android.app.action.TRANSFER_OWNERSHIP_COMPLETE";
+
+ /**
+ * A {@link android.os.Parcelable} extra of type {@link android.os.PersistableBundle} that
+ * allows a mobile device management application to pass data to the management application
+ * instance after owner transfer.
+ *
+ * <p>
+ * If the transfer is successful, the new device owner receives the data in
+ * {@link DeviceAdminReceiver#onTransferOwnershipComplete(Context, PersistableBundle)}.
+ * The bundle is not changed during the ownership transfer.
+ *
+ * @see DevicePolicyManager#transferOwnership(ComponentName, ComponentName, PersistableBundle)
+ */
+ public static final String EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE =
+ "android.app.extra.TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE";
+
+ /**
+ * Name under which a device administration component indicates whether it supports transfer of
+ * ownership. This meta-data is of type <code>boolean</code>. A value of <code>true</code>
+ * allows this administrator to be used as a target administrator for a transfer. If the value
+ * is <code>false</code>, ownership cannot be transferred to this administrator. The default
+ * value is <code>false</code>.
+ * <p>This metadata is used to avoid ownership transfer migration to an administrator with a
+ * version which does not yet support it.
+ * <p>Usage:
+ * <pre>
+ * <receiver name="..." android:permission="android.permission.BIND_DEVICE_ADMIN">
+ * <meta-data
+ * android:name="android.app.device_admin"
+ * android:resource="@xml/..." />
+ * <meta-data
+ * android:name="android.app.support_transfer_ownership"
+ * android:value="true" />
+ * </receiver>
+ * </pre>
+ *
+ * @see DevicePolicyManager#transferOwnership(ComponentName, ComponentName, PersistableBundle)
+ */
+ public static final String SUPPORT_TRANSFER_OWNERSHIP_META_DATA =
+ "android.app.support_transfer_ownership";
+
private DevicePolicyManager mManager;
private ComponentName mWho;
@@ -860,6 +914,20 @@
}
/**
+ * Called on the newly assigned owner (either device owner or profile owner) when the ownership
+ * transfer has completed successfully.
+ *
+ * <p> The {@code bundle} parameter allows the original owner to pass data
+ * to the new one.
+ *
+ * @param context the running context as per {@link #onReceive}
+ * @param bundle the data to be passed to the new owner
+ */
+ public void onTransferOwnershipComplete(@NonNull Context context,
+ @Nullable PersistableBundle bundle) {
+ }
+
+ /**
* Intercept standard device administrator broadcasts. Implementations
* should not override this method; it is better to implement the
* convenience callbacks for each action.
@@ -921,6 +989,10 @@
onUserAdded(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER));
} else if (ACTION_USER_REMOVED.equals(action)) {
onUserRemoved(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER));
+ } else if (ACTION_TRANSFER_OWNERSHIP_COMPLETE.equals(action)) {
+ PersistableBundle bundle =
+ intent.getParcelableExtra(EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE);
+ onTransferOwnershipComplete(context, bundle);
}
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 76c078e..9329d56 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -18,7 +18,6 @@
import android.annotation.CallbackExecutor;
import android.annotation.ColorInt;
-import android.annotation.Condemned;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -50,8 +49,6 @@
import android.net.ProxyInfo;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.Process;
@@ -1124,6 +1121,7 @@
*
* This broadcast is sent only to the primary user.
* @see #ACTION_PROVISION_MANAGED_DEVICE
+ * @see DevicePolicyManager#transferOwnership(ComponentName, ComponentName, PersistableBundle)
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_DEVICE_OWNER_CHANGED
@@ -1253,6 +1251,26 @@
= "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED";
/**
+ * Broadcast action to notify ManagedProvisioning that
+ * {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE} restriction has changed.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_DATA_SHARING_RESTRICTION_CHANGED =
+ "android.app.action.DATA_SHARING_RESTRICTION_CHANGED";
+
+ /**
+ * Broadcast action from ManagedProvisioning to notify that the latest change to
+ * {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE} restriction has been successfully
+ * applied (cross profile intent filters updated). Only usesd for CTS tests.
+ * @hide
+ */
+ @TestApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED =
+ "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
+
+ /**
* Permission policy to prompt user for new permission requests for runtime permissions.
* Already granted or denied permissions are not affected by this.
*/
@@ -1709,6 +1727,16 @@
public static final int ID_TYPE_MEID = 8;
/**
+ * Broadcast action: sent when the profile owner is set, changed or cleared.
+ *
+ * This broadcast is sent only to the user managed by the new profile owner.
+ * @see DevicePolicyManager#transferOwnership(ComponentName, ComponentName, PersistableBundle)
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PROFILE_OWNER_CHANGED =
+ "android.app.action.PROFILE_OWNER_CHANGED";
+
+ /**
* Return true if the given administrator component is currently active (enabled) in the system.
*
* @param admin The administrator component to check for.
@@ -6046,6 +6074,13 @@
* Called by a profile owner of a managed profile to remove the cross-profile intent filters
* that go from the managed profile to the parent, or from the parent to the managed profile.
* Only removes those that have been set by the profile owner.
+ * <p>
+ * <em>Note</em>: A list of default cross profile intent filters are set up by the system when
+ * the profile is created, some of them ensure the proper functioning of the profile, while
+ * others enable sharing of data from the parent to the managed profile for user convenience.
+ * These default intent filters are not cleared when this API is called. If the default cross
+ * profile data sharing is not desired, they can be disabled with
+ * {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE}.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @throws SecurityException if {@code admin} is not a device or profile owner.
@@ -6468,12 +6503,6 @@
public static final int MAKE_USER_DEMO = 0x0004;
/**
- * Flag used by {@link #createAndManageUser} to specify that the newly created user should be
- * started in the background as part of the user creation.
- */
- public static final int START_USER_IN_BACKGROUND = 0x0008;
-
- /**
* Flag used by {@link #createAndManageUser} to specify that the newly created user should skip
* the disabling of system apps during provisioning.
*/
@@ -6486,7 +6515,6 @@
SKIP_SETUP_WIZARD,
MAKE_USER_EPHEMERAL,
MAKE_USER_DEMO,
- START_USER_IN_BACKGROUND,
LEAVE_ALL_SYSTEM_APPS_ENABLED
})
@Retention(RetentionPolicy.SOURCE)
@@ -6515,7 +6543,8 @@
* IllegalArgumentException is thrown.
* @param adminExtras Extras that will be passed to onEnable of the admin receiver on the new
* user.
- * @param flags {@link #SKIP_SETUP_WIZARD} is supported.
+ * @param flags {@link #SKIP_SETUP_WIZARD}, {@link #MAKE_USER_EPHEMERAL} and
+ * {@link #LEAVE_ALL_SYSTEM_APPS_ENABLED} are supported.
* @see UserHandle
* @return the {@link android.os.UserHandle} object for the created user, or {@code null} if the
* user could not be created.
@@ -6534,8 +6563,8 @@
}
/**
- * Called by a device owner to remove a user and all associated data. The primary user can not
- * be removed.
+ * Called by a device owner to remove a user/profile and all associated data. The primary user
+ * can not be removed.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param userHandle the user to remove.
@@ -6552,14 +6581,14 @@
}
/**
- * Called by a device owner to switch the specified user to the foreground.
- * <p> This cannot be used to switch to a managed profile.
+ * Called by a device owner to switch the specified secondary user to the foreground.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param userHandle the user to switch to; null will switch to primary.
* @return {@code true} if the switch was successful, {@code false} otherwise.
* @throws SecurityException if {@code admin} is not a device owner.
* @see Intent#ACTION_USER_FOREGROUND
+ * @see #getSecondaryUsers(ComponentName)
*/
public boolean switchUser(@NonNull ComponentName admin, @Nullable UserHandle userHandle) {
throwIfParentInstance("switchUser");
@@ -6571,13 +6600,32 @@
}
/**
+ * Called by a device owner to start the specified secondary user in background.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param userHandle the user to be stopped.
+ * @return {@code true} if the user can be started, {@code false} otherwise.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ * @see #getSecondaryUsers(ComponentName)
+ */
+ public boolean startUserInBackground(
+ @NonNull ComponentName admin, @NonNull UserHandle userHandle) {
+ throwIfParentInstance("startUserInBackground");
+ try {
+ return mService.startUserInBackground(admin, userHandle);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Called by a device owner to stop the specified secondary user.
- * <p> This cannot be used to stop the primary user or a managed profile.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param userHandle the user to be stopped.
* @return {@code true} if the user can be stopped, {@code false} otherwise.
* @throws SecurityException if {@code admin} is not a device owner.
+ * @see #getSecondaryUsers(ComponentName)
*/
public boolean stopUser(@NonNull ComponentName admin, @NonNull UserHandle userHandle) {
throwIfParentInstance("stopUser");
@@ -6589,14 +6637,13 @@
}
/**
- * Called by a profile owner that is affiliated with the device to stop the calling user
- * and switch back to primary.
- * <p> This has no effect when called on a managed profile.
+ * Called by a profile owner of secondary user that is affiliated with the device to stop the
+ * calling user and switch back to primary.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @return {@code true} if the exit was successful, {@code false} otherwise.
* @throws SecurityException if {@code admin} is not a profile owner affiliated with the device.
- * @see #isAffiliatedUser
+ * @see #getSecondaryUsers(ComponentName)
*/
public boolean logoutUser(@NonNull ComponentName admin) {
throwIfParentInstance("logoutUser");
@@ -6608,17 +6655,18 @@
}
/**
- * Called by a device owner to list all secondary users on the device, excluding managed
- * profiles.
+ * Called by a device owner to list all secondary users on the device. Managed profiles are not
+ * considered as secondary users.
* <p> Used for various user management APIs, including {@link #switchUser}, {@link #removeUser}
* and {@link #stopUser}.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @return list of other {@link UserHandle}s on the device.
* @throws SecurityException if {@code admin} is not a device owner.
- * @see #switchUser
- * @see #removeUser
- * @see #stopUser
+ * @see #removeUser(ComponentName, UserHandle)
+ * @see #switchUser(ComponentName, UserHandle)
+ * @see #startUserInBackground(ComponentName, UserHandle)
+ * @see #stopUser(ComponentName, UserHandle)
*/
public List<UserHandle> getSecondaryUsers(@NonNull ComponentName admin) {
throwIfParentInstance("getSecondaryUsers");
@@ -8582,6 +8630,13 @@
*
* <p> Backup service is off by default when device owner is present.
*
+ * <p> If backups are made mandatory by specifying a non-null mandatory backup transport using
+ * the {@link DevicePolicyManager#setMandatoryBackupTransport} method, the backup service is
+ * automatically enabled.
+ *
+ * <p> If the backup service is disabled using this method after the mandatory backup transport
+ * has been set, the mandatory backup transport is cleared.
+ *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param enabled {@code true} to enable the backup service, {@code false} to disable it.
* @throws SecurityException if {@code admin} is not a device owner.
@@ -8613,6 +8668,43 @@
}
/**
+ * Makes backups mandatory and enforces the usage of the specified backup transport.
+ *
+ * <p>When a {@code null} backup transport is specified, backups are made optional again.
+ * <p>Only device owner can call this method.
+ * <p>If backups were disabled and a non-null backup transport {@link ComponentName} is
+ * specified, backups will be enabled.
+ *
+ * @param admin admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param backupTransportComponent The backup transport layer to be used for mandatory backups.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public void setMandatoryBackupTransport(
+ @NonNull ComponentName admin, @Nullable ComponentName backupTransportComponent) {
+ try {
+ mService.setMandatoryBackupTransport(admin, backupTransportComponent);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the backup transport which has to be used for backups if backups are mandatory or
+ * {@code null} if backups are not mandatory.
+ *
+ * @return a {@link ComponentName} of the backup transport layer to be used if backups are
+ * mandatory or {@code null} if backups are not mandatory.
+ */
+ public ComponentName getMandatoryBackupTransport() {
+ try {
+ return mService.getMandatoryBackupTransport();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+
+ /**
* Called by a device owner to control the network logging feature.
*
* <p> Network logs contain DNS lookup and connect() library call events. The following library
@@ -8888,15 +8980,6 @@
}
}
- /** {@hide} */
- @Condemned
- @Deprecated
- public boolean clearApplicationUserData(@NonNull ComponentName admin,
- @NonNull String packageName, @NonNull OnClearApplicationUserDataListener listener,
- @NonNull Handler handler) {
- return clearApplicationUserData(admin, packageName, listener, new HandlerExecutor(handler));
- }
-
/**
* Called by the device owner or profile owner to clear application user data of a given
* package. The behaviour of this is equivalent to the target application calling
@@ -8907,14 +8990,14 @@
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param packageName The name of the package which will have its user data wiped.
- * @param listener A callback object that will inform the caller when the clearing is done.
* @param executor The executor through which the listener should be invoked.
+ * @param listener A callback object that will inform the caller when the clearing is done.
* @throws SecurityException if the caller is not the device owner/profile owner.
* @return whether the clearing succeeded.
*/
public boolean clearApplicationUserData(@NonNull ComponentName admin,
- @NonNull String packageName, @NonNull OnClearApplicationUserDataListener listener,
- @NonNull @CallbackExecutor Executor executor) {
+ @NonNull String packageName, @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnClearApplicationUserDataListener listener) {
throwIfParentInstance("clearAppData");
Preconditions.checkNotNull(executor);
try {
@@ -8997,41 +9080,119 @@
}
}
- //TODO STOPSHIP Add link to onTransferComplete callback when implemented.
/**
- * Transfers the current administrator. All policies from the current administrator are
- * migrated to the new administrator. The whole operation is atomic - the transfer is either
- * complete or not done at all.
+ * Changes the current administrator to another one. All policies from the current
+ * administrator are migrated to the new administrator. The whole operation is atomic -
+ * the transfer is either complete or not done at all.
*
- * Depending on the current administrator (device owner, profile owner, corporate owned
- * profile owner), you have the following expected behaviour:
+ * <p>Depending on the current administrator (device owner, profile owner), you have the
+ * following expected behaviour:
* <ul>
* <li>A device owner can only be transferred to a new device owner</li>
* <li>A profile owner can only be transferred to a new profile owner</li>
- * <li>A corporate owned managed profile can have two cases:
- * <ul>
- * <li>If the device owner and profile owner are the same package,
- * both will be transferred.</li>
- * <li>If the device owner and profile owner are different packages,
- * and if this method is called from the profile owner, only the profile owner
- * is transferred. Similarly, if it is called from the device owner, only
- * the device owner is transferred.</li>
- * </ul>
- * </li>
* </ul>
*
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @param target Which {@link DeviceAdminReceiver} we want the new administrator to be.
- * @param bundle Parameters - This bundle allows the current administrator to pass data to the
- * new administrator. The parameters will be received in the
- * onTransferComplete callback.
- * @hide
+ * <p>Use the {@code bundle} parameter to pass data to the new administrator. The parameters
+ * will be received in the
+ * {@link DeviceAdminReceiver#onTransferOwnershipComplete(Context, PersistableBundle)} callback.
+ *
+ * <p>The incoming target administrator must have the
+ * {@link DeviceAdminReceiver#SUPPORT_TRANSFER_OWNERSHIP_META_DATA} <code>meta-data</code> tag
+ * included in its corresponding <code>receiver</code> component with a value of {@code true}.
+ * Otherwise an {@link IllegalArgumentException} will be thrown.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with
+ * @param target which {@link DeviceAdminReceiver} we want the new administrator to be
+ * @param bundle data to be sent to the new administrator
+ * @throws SecurityException if {@code admin} is not a device owner nor a profile owner
+ * @throws IllegalArgumentException if {@code admin} or {@code target} is {@code null},
+ * both are components in the same package or {@code target} is not an active admin
*/
- public void transferOwner(@NonNull ComponentName admin, @NonNull ComponentName target,
+ public void transferOwnership(@NonNull ComponentName admin, @NonNull ComponentName target,
PersistableBundle bundle) {
- throwIfParentInstance("transferOwner");
+ throwIfParentInstance("transferOwnership");
try {
- mService.transferOwner(admin, target, bundle);
+ mService.transferOwnership(admin, target, bundle);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Called by a device owner to specify the user session start message. This may be displayed
+ * during a user switch.
+ * <p>
+ * The message should be limited to a short statement or it may be truncated.
+ * <p>
+ * If the message needs to be localized, it is the responsibility of the
+ * {@link DeviceAdminReceiver} to listen to the {@link Intent#ACTION_LOCALE_CHANGED} broadcast
+ * and set a new version of this message accordingly.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+ * @param startUserSessionMessage message for starting user session, or {@code null} to use
+ * system default message.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public void setStartUserSessionMessage(
+ @NonNull ComponentName admin, @Nullable CharSequence startUserSessionMessage) {
+ throwIfParentInstance("setStartUserSessionMessage");
+ try {
+ mService.setStartUserSessionMessage(admin, startUserSessionMessage);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Called by a device owner to specify the user session end message. This may be displayed
+ * during a user switch.
+ * <p>
+ * The message should be limited to a short statement or it may be truncated.
+ * <p>
+ * If the message needs to be localized, it is the responsibility of the
+ * {@link DeviceAdminReceiver} to listen to the {@link Intent#ACTION_LOCALE_CHANGED} broadcast
+ * and set a new version of this message accordingly.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+ * @param endUserSessionMessage message for ending user session, or {@code null} to use system
+ * default message.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public void setEndUserSessionMessage(
+ @NonNull ComponentName admin, @Nullable CharSequence endUserSessionMessage) {
+ throwIfParentInstance("setEndUserSessionMessage");
+ try {
+ mService.setEndUserSessionMessage(admin, endUserSessionMessage);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the user session start message.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public CharSequence getStartUserSessionMessage(@NonNull ComponentName admin) {
+ throwIfParentInstance("getStartUserSessionMessage");
+ try {
+ return mService.getStartUserSessionMessage(admin);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the user session end message.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public CharSequence getEndUserSessionMessage(@NonNull ComponentName admin) {
+ throwIfParentInstance("getEndUserSessionMessage");
+ try {
+ return mService.getEndUserSessionMessage(admin);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index b692ffd9..531bef0 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -123,4 +123,13 @@
* @param userId User ID of the profile.
*/
public abstract void reportSeparateProfileChallengeChanged(@UserIdInt int userId);
+
+ /**
+ * Check whether the user could have their password reset in an untrusted manor due to there
+ * being an admin which can call {@link #resetPassword} to reset the password without knowledge
+ * of the previous password.
+ *
+ * @param userId The user in question
+ */
+ public abstract boolean canUserHaveUntrustedCredentialReset(@UserIdInt int userId);
}
diff --git a/core/java/android/app/admin/DnsEvent.java b/core/java/android/app/admin/DnsEvent.java
index 4ddf13e..a2d704b 100644
--- a/core/java/android/app/admin/DnsEvent.java
+++ b/core/java/android/app/admin/DnsEvent.java
@@ -96,7 +96,7 @@
@Override
public String toString() {
- return String.format("DnsEvent(%s, %s, %d, %d, %s)", mHostname,
+ return String.format("DnsEvent(%d, %s, %s, %d, %d, %s)", mId, mHostname,
(mIpAddresses == null) ? "NONE" : String.join(" ", mIpAddresses),
mIpAddressesCount, mTimestamp, mPackageName);
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 5916a62..eac7f7e 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -226,6 +226,7 @@
UserHandle createAndManageUser(in ComponentName who, in String name, in ComponentName profileOwner, in PersistableBundle adminExtras, in int flags);
boolean removeUser(in ComponentName who, in UserHandle userHandle);
boolean switchUser(in ComponentName who, in UserHandle userHandle);
+ boolean startUserInBackground(in ComponentName who, in UserHandle userHandle);
boolean stopUser(in ComponentName who, in UserHandle userHandle);
boolean logoutUser(in ComponentName who);
List<UserHandle> getSecondaryUsers(in ComponentName who);
@@ -358,6 +359,8 @@
void setBackupServiceEnabled(in ComponentName admin, boolean enabled);
boolean isBackupServiceEnabled(in ComponentName admin);
+ void setMandatoryBackupTransport(in ComponentName admin, in ComponentName backupTransportComponent);
+ ComponentName getMandatoryBackupTransport();
void setNetworkLoggingEnabled(in ComponentName admin, boolean enabled);
boolean isNetworkLoggingEnabled(in ComponentName admin);
@@ -387,5 +390,10 @@
boolean isLogoutEnabled();
List<String> getDisallowedSystemApps(in ComponentName admin, int userId, String provisioningAction);
- void transferOwner(in ComponentName admin, in ComponentName target, in PersistableBundle bundle);
+ void transferOwnership(in ComponentName admin, in ComponentName target, in PersistableBundle bundle);
+
+ void setStartUserSessionMessage(in ComponentName admin, in CharSequence startUserSessionMessage);
+ void setEndUserSessionMessage(in ComponentName admin, in CharSequence endUserSessionMessage);
+ CharSequence getStartUserSessionMessage(in ComponentName admin);
+ CharSequence getEndUserSessionMessage(in ComponentName admin);
}
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index 3a6a5b2..12f4483 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -27,6 +27,7 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.util.Log;
import android.util.Pair;
@@ -387,6 +388,29 @@
}
/**
+ * Report whether the backup mechanism is currently active.
+ * When it is inactive, the device will not perform any backup operations, nor will it
+ * deliver data for restore, although clients can still safely call BackupManager methods.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BACKUP)
+ public boolean isBackupServiceActive(UserHandle user) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+ "isBackupServiceActive");
+ checkServiceBinder();
+ if (sService != null) {
+ try {
+ return sService.isBackupServiceActive(user.getIdentifier());
+ } catch (RemoteException e) {
+ Log.e(TAG, "isBackupEnabled() couldn't connect");
+ }
+ }
+ return false;
+ }
+
+ /**
* Enable/disable data restore at application install time. When enabled, app
* installation will include an attempt to fetch the app's historical data from
* the archival restore dataset (if any). When disabled, no such attempt will
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
index da81d19..3558e34 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -55,6 +55,22 @@
// Transport should ignore its own moratoriums for call with this flag set.
public static final int FLAG_USER_INITIATED = 1;
+ /**
+ * For key value backup, indicates that the backup data is a diff from a previous backup. The
+ * transport must apply this diff to an existing backup to build the new backup set.
+ *
+ * @hide
+ */
+ public static final int FLAG_INCREMENTAL = 1 << 1;
+
+ /**
+ * For key value backup, indicates that the backup data is a complete set, not a diff from a
+ * previous backup. The transport should clear any previous backup when storing this backup.
+ *
+ * @hide
+ */
+ public static final int FLAG_NON_INCREMENTAL = 1 << 2;
+
IBackupTransport mBinderImpl = new TransportImpl();
public IBinder getBinder() {
@@ -231,12 +247,18 @@
* {@link #TRANSPORT_OK}, {@link #finishBackup} will then be called to ensure the data
* is sent and recorded successfully.
*
+ * If the backup data is a diff against the previous backup then the flag {@link
+ * BackupTransport#FLAG_INCREMENTAL} will be set. Otherwise, if the data is a complete backup
+ * set then {@link BackupTransport#FLAG_NON_INCREMENTAL} will be set. Before P neither flag will
+ * be set regardless of whether the backup is incremental or not.
+ *
* @param packageInfo The identity of the application whose data is being backed up.
* This specifically includes the signature list for the package.
* @param inFd Descriptor of file with data that resulted from invoking the application's
* BackupService.doBackup() method. This may be a pipe rather than a file on
* persistent media, so it may not be seekable.
- * @param flags {@link BackupTransport#FLAG_USER_INITIATED} or 0.
+ * @param flags a combination of {@link BackupTransport#FLAG_USER_INITIATED}, {@link
+ * BackupTransport#FLAG_NON_INCREMENTAL}, {@link BackupTransport#FLAG_INCREMENTAL}, or 0.
* @return one of {@link BackupTransport#TRANSPORT_OK} (OK so far),
* {@link BackupTransport#TRANSPORT_PACKAGE_REJECTED} (to suppress backup of this
* specific package, but allow others to proceed),
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 792cb5f..f3ca746 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -294,7 +294,8 @@
*
* @param transport ComponentName of the service hosting the transport. This is different from
* the transport's name that is returned by {@link BackupTransport#name()}.
- * @param listener A listener object to get a callback on the transport being selected.
+ * @param listener A listener object to get a callback on the transport being selected. It may
+ * be {@code null}.
*
* @hide
*/
diff --git a/core/java/android/app/job/JobParameters.java b/core/java/android/app/job/JobParameters.java
index 5053dc6..c71bf2e 100644
--- a/core/java/android/app/job/JobParameters.java
+++ b/core/java/android/app/job/JobParameters.java
@@ -70,6 +70,7 @@
private final Network network;
private int stopReason; // Default value of stopReason is REASON_CANCELED
+ private String debugStopReason; // Human readable stop reason for debugging.
/** @hide */
public JobParameters(IBinder callback, int jobId, PersistableBundle extras,
@@ -104,6 +105,14 @@
}
/**
+ * Reason onStopJob() was called on this job.
+ * @hide
+ */
+ public String getDebugStopReason() {
+ return debugStopReason;
+ }
+
+ /**
* @return The extras you passed in when constructing this job with
* {@link android.app.job.JobInfo.Builder#setExtras(android.os.PersistableBundle)}. This will
* never be null. If you did not set any extras this will be an empty bundle.
@@ -288,11 +297,13 @@
network = null;
}
stopReason = in.readInt();
+ debugStopReason = in.readString();
}
/** @hide */
- public void setStopReason(int reason) {
+ public void setStopReason(int reason, String debugStopReason) {
stopReason = reason;
+ this.debugStopReason = debugStopReason;
}
@Override
@@ -323,6 +334,7 @@
dest.writeInt(0);
}
dest.writeInt(stopReason);
+ dest.writeString(debugStopReason);
}
public static final Creator<JobParameters> CREATOR = new Creator<JobParameters>() {
diff --git a/core/java/android/app/servertransaction/ActivityLifecycleItem.java b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
index 0fdc7c5..9a50a00 100644
--- a/core/java/android/app/servertransaction/ActivityLifecycleItem.java
+++ b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
@@ -17,7 +17,9 @@
package android.app.servertransaction;
import android.annotation.IntDef;
+import android.os.Parcel;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -26,6 +28,7 @@
* @hide
*/
public abstract class ActivityLifecycleItem extends ClientTransactionItem {
+ private String mDescription;
@IntDef(prefix = { "UNDEFINED", "PRE_", "ON_" }, value = {
UNDEFINED,
@@ -53,4 +56,39 @@
/** A final lifecycle state that an activity should reach. */
@LifecycleState
public abstract int getTargetState();
+
+
+ protected ActivityLifecycleItem() {
+ }
+
+ protected ActivityLifecycleItem(Parcel in) {
+ mDescription = in.readString();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mDescription);
+ }
+
+ /**
+ * Sets a description that can be retrieved later for debugging purposes.
+ * @param description Description to set.
+ * @return The {@link ActivityLifecycleItem}.
+ */
+ public ActivityLifecycleItem setDescription(String description) {
+ mDescription = description;
+ return this;
+ }
+
+ /**
+ * Retrieves description if set through {@link #setDescription(String)}.
+ */
+ public String getDescription() {
+ return mDescription;
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "target state:" + getTargetState());
+ pw.println(prefix + "description: " + mDescription);
+ }
}
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 764ceed..fc07879 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -24,6 +24,9 @@
import android.os.Parcelable;
import android.os.RemoteException;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -54,6 +57,11 @@
/** Target client activity. Might be null if the entire transaction is targeting an app. */
private IBinder mActivityToken;
+ /** Get the target client of the transaction. */
+ public IApplicationThread getClient() {
+ return mClient;
+ }
+
/**
* Add a message to the end of the sequence of callbacks.
* @param activityCallback A single message that can contain a lifecycle request/callback.
@@ -78,7 +86,8 @@
}
/** Get the target state lifecycle request. */
- ActivityLifecycleItem getLifecycleStateRequest() {
+ @VisibleForTesting
+ public ActivityLifecycleItem getLifecycleStateRequest() {
return mLifecycleStateRequest;
}
@@ -229,4 +238,12 @@
result = 31 * result + Objects.hashCode(mLifecycleStateRequest);
return result;
}
+
+ void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "mActivityToken:" + mActivityToken.hashCode());
+ pw.println(prefix + "mLifecycleStateRequest:");
+ if (mLifecycleStateRequest != null) {
+ mLifecycleStateRequest.dump(pw, prefix + " ");
+ }
+ }
}
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
index 83da5f3..cbcf6c7 100644
--- a/core/java/android/app/servertransaction/DestroyActivityItem.java
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -76,12 +76,14 @@
/** Write to Parcel. */
@Override
public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
dest.writeBoolean(mFinished);
dest.writeInt(mConfigChanges);
}
/** Read from Parcel. */
private DestroyActivityItem(Parcel in) {
+ super(in);
mFinished = in.readBoolean();
mConfigChanges = in.readInt();
}
diff --git a/core/java/android/app/servertransaction/ObjectPool.java b/core/java/android/app/servertransaction/ObjectPool.java
index 9812125..2fec30a 100644
--- a/core/java/android/app/servertransaction/ObjectPool.java
+++ b/core/java/android/app/servertransaction/ObjectPool.java
@@ -16,8 +16,8 @@
package android.app.servertransaction;
+import java.util.ArrayList;
import java.util.HashMap;
-import java.util.LinkedList;
import java.util.Map;
/**
@@ -27,7 +27,7 @@
class ObjectPool {
private static final Object sPoolSync = new Object();
- private static final Map<Class, LinkedList<? extends ObjectPoolItem>> sPoolMap =
+ private static final Map<Class, ArrayList<? extends ObjectPoolItem>> sPoolMap =
new HashMap<>();
private static final int MAX_POOL_SIZE = 50;
@@ -40,9 +40,9 @@
public static <T extends ObjectPoolItem> T obtain(Class<T> itemClass) {
synchronized (sPoolSync) {
@SuppressWarnings("unchecked")
- LinkedList<T> itemPool = (LinkedList<T>) sPoolMap.get(itemClass);
+ final ArrayList<T> itemPool = (ArrayList<T>) sPoolMap.get(itemClass);
if (itemPool != null && !itemPool.isEmpty()) {
- return itemPool.poll();
+ return itemPool.remove(itemPool.size() - 1);
}
return null;
}
@@ -56,16 +56,20 @@
public static <T extends ObjectPoolItem> void recycle(T item) {
synchronized (sPoolSync) {
@SuppressWarnings("unchecked")
- LinkedList<T> itemPool = (LinkedList<T>) sPoolMap.get(item.getClass());
+ ArrayList<T> itemPool = (ArrayList<T>) sPoolMap.get(item.getClass());
if (itemPool == null) {
- itemPool = new LinkedList<>();
+ itemPool = new ArrayList<>();
sPoolMap.put(item.getClass(), itemPool);
}
- if (itemPool.contains(item)) {
- throw new IllegalStateException("Trying to recycle already recycled item");
+ // Check if the item is already in the pool
+ final int size = itemPool.size();
+ for (int i = 0; i < size; i++) {
+ if (itemPool.get(i) == item) {
+ throw new IllegalStateException("Trying to recycle already recycled item");
+ }
}
- if (itemPool.size() < MAX_POOL_SIZE) {
+ if (size < MAX_POOL_SIZE) {
itemPool.add(item);
}
}
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
index 880fef7..70a4755 100644
--- a/core/java/android/app/servertransaction/PauseActivityItem.java
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -114,6 +114,7 @@
/** Write to Parcel. */
@Override
public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
dest.writeBoolean(mFinished);
dest.writeBoolean(mUserLeaving);
dest.writeInt(mConfigChanges);
@@ -122,6 +123,7 @@
/** Read from Parcel. */
private PauseActivityItem(Parcel in) {
+ super(in);
mFinished = in.readBoolean();
mUserLeaving = in.readBoolean();
mConfigChanges = in.readInt();
diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java
index 9249c6e..ed90f2c 100644
--- a/core/java/android/app/servertransaction/ResumeActivityItem.java
+++ b/core/java/android/app/servertransaction/ResumeActivityItem.java
@@ -113,6 +113,7 @@
/** Write to Parcel. */
@Override
public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
dest.writeInt(mProcState);
dest.writeBoolean(mUpdateProcState);
dest.writeBoolean(mIsForward);
@@ -120,6 +121,7 @@
/** Read from Parcel. */
private ResumeActivityItem(Parcel in) {
+ super(in);
mProcState = in.readInt();
mUpdateProcState = in.readBoolean();
mIsForward = in.readBoolean();
diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java
index 5c5c304..b814d1a 100644
--- a/core/java/android/app/servertransaction/StopActivityItem.java
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -83,12 +83,14 @@
/** Write to Parcel. */
@Override
public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
dest.writeBoolean(mShowWindow);
dest.writeInt(mConfigChanges);
}
/** Read from Parcel. */
private StopActivityItem(Parcel in) {
+ super(in);
mShowWindow = in.readBoolean();
mConfigChanges = in.readInt();
}
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 5b0ea6b..78b393a 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -33,6 +33,8 @@
import com.android.internal.annotations.VisibleForTesting;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.List;
/**
@@ -122,6 +124,21 @@
final IBinder token = transaction.getActivityToken();
final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
+ // TODO(b/71506345): Remove once root cause is found.
+ if (r == null) {
+ final StringWriter stringWriter = new StringWriter();
+ final PrintWriter pw = new PrintWriter(stringWriter);
+ final String prefix = " ";
+
+ pw.println("Lifecycle transaction does not have valid ActivityClientRecord.");
+ pw.println("Transaction:");
+ transaction.dump(pw, prefix);
+ pw.println("Executor:");
+ dump(pw, prefix);
+
+ Slog.wtf(TAG, stringWriter.toString());
+ }
+
// Cycle to the state right before the final requested state.
cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */);
@@ -245,4 +262,9 @@
private static void log(String message) {
if (DEBUG_RESOLVER) Slog.d(TAG, message);
}
+
+ private void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "mTransactionHandler:");
+ mTransactionHandler.dump(pw, prefix + " ");
+ }
}
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index f8093d5..6093a4a 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -21,12 +21,10 @@
import android.annotation.StringDef;
import android.app.PendingIntent;
import android.app.RemoteInput;
-import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IContentProvider;
import android.content.Intent;
-import android.content.pm.ResolveInfo;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
@@ -558,16 +556,11 @@
}
/**
- * Turns a slice Uri into slice content.
- *
- * @param resolver ContentResolver to be used.
- * @param uri The URI to a slice provider
- * @param supportedSpecs List of supported specs.
- * @return The Slice provided by the app or null if none is given.
- * @see Slice
+ * @deprecated TO BE REMOVED.
*/
- public static @Nullable Slice bindSlice(ContentResolver resolver, @NonNull Uri uri,
- List<SliceSpec> supportedSpecs) {
+ @Deprecated
+ public static @Nullable Slice bindSlice(ContentResolver resolver,
+ @NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
Preconditions.checkNotNull(uri, "uri");
IContentProvider provider = resolver.acquireProvider(uri);
if (provider == null) {
@@ -595,60 +588,11 @@
}
/**
- * Turns a slice intent into slice content. Expects an explicit intent. If there is no
- * {@link ContentProvider} associated with the given intent this will throw
- * {@link IllegalArgumentException}.
- *
- * @param context The context to use.
- * @param intent The intent associated with a slice.
- * @param supportedSpecs List of supported specs.
- * @return The Slice provided by the app or null if none is given.
- * @see Slice
- * @see SliceProvider#onMapIntentToUri(Intent)
- * @see Intent
+ * @deprecated TO BE REMOVED.
*/
+ @Deprecated
public static @Nullable Slice bindSlice(Context context, @NonNull Intent intent,
- List<SliceSpec> supportedSpecs) {
- Preconditions.checkNotNull(intent, "intent");
- Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null,
- "Slice intent must be explicit " + intent);
- ContentResolver resolver = context.getContentResolver();
-
- // Check if the intent has data for the slice uri on it and use that
- final Uri intentData = intent.getData();
- if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
- return bindSlice(resolver, intentData, supportedSpecs);
- }
- // Otherwise ask the app
- List<ResolveInfo> providers =
- context.getPackageManager().queryIntentContentProviders(intent, 0);
- if (providers == null) {
- throw new IllegalArgumentException("Unable to resolve intent " + intent);
- }
- String authority = providers.get(0).providerInfo.authority;
- Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
- .authority(authority).build();
- IContentProvider provider = resolver.acquireProvider(uri);
- if (provider == null) {
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- try {
- Bundle extras = new Bundle();
- extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
- extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
- new ArrayList<>(supportedSpecs));
- final Bundle res = provider.call(resolver.getPackageName(),
- SliceProvider.METHOD_MAP_INTENT, null, extras);
- if (res == null) {
- return null;
- }
- return res.getParcelable(SliceProvider.EXTRA_SLICE);
- } catch (RemoteException e) {
- // Arbitrary and not worth documenting, as Activity
- // Manager will kill this process shortly anyway.
- return null;
- } finally {
- resolver.releaseProvider(provider);
- }
+ @NonNull List<SliceSpec> supportedSpecs) {
+ return context.getSystemService(SliceManager.class).bindSlice(intent, supportedSpecs);
}
}
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 0c5f225d..74864cb 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -17,17 +17,29 @@
package android.app.slice;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemService;
+import android.content.ContentResolver;
import android.content.Context;
+import android.content.IContentProvider;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.Pair;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
@@ -39,6 +51,8 @@
@SystemService(Context.SLICE_SERVICE)
public class SliceManager {
+ private static final String TAG = "SliceManager";
+
private final ISliceManager mService;
private final Context mContext;
private final ArrayMap<Pair<Uri, SliceCallback>, ISliceListener> mListenerLookup =
@@ -224,6 +238,126 @@
}
/**
+ * Obtains a list of slices that are descendants of the specified Uri.
+ * <p>
+ * Not all slice providers will implement this functionality, in which case,
+ * an empty collection will be returned.
+ *
+ * @param uri The uri to look for descendants under.
+ * @return All slices within the space.
+ * @see SliceProvider#onGetSliceDescendants(Uri)
+ */
+ public @NonNull Collection<Uri> getSliceDescendants(@NonNull Uri uri) {
+ ContentResolver resolver = mContext.getContentResolver();
+ IContentProvider provider = resolver.acquireProvider(uri);
+ try {
+ Bundle extras = new Bundle();
+ extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
+ final Bundle res = provider.call(resolver.getPackageName(),
+ SliceProvider.METHOD_GET_DESCENDANTS, null, extras);
+ return res.getParcelableArrayList(SliceProvider.EXTRA_SLICE_DESCENDANTS);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to get slice descendants", e);
+ } finally {
+ resolver.releaseProvider(provider);
+ }
+ return Collections.emptyList();
+ }
+
+ /**
+ * Turns a slice Uri into slice content.
+ *
+ * @param uri The URI to a slice provider
+ * @param supportedSpecs List of supported specs.
+ * @return The Slice provided by the app or null if none is given.
+ * @see Slice
+ */
+ public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
+ Preconditions.checkNotNull(uri, "uri");
+ ContentResolver resolver = mContext.getContentResolver();
+ IContentProvider provider = resolver.acquireProvider(uri);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ try {
+ Bundle extras = new Bundle();
+ extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
+ extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+ new ArrayList<>(supportedSpecs));
+ final Bundle res = provider.call(resolver.getPackageName(), SliceProvider.METHOD_SLICE,
+ null, extras);
+ Bundle.setDefusable(res, true);
+ if (res == null) {
+ return null;
+ }
+ return res.getParcelable(SliceProvider.EXTRA_SLICE);
+ } catch (RemoteException e) {
+ // Arbitrary and not worth documenting, as Activity
+ // Manager will kill this process shortly anyway.
+ return null;
+ } finally {
+ resolver.releaseProvider(provider);
+ }
+ }
+
+ /**
+ * Turns a slice intent into slice content. Expects an explicit intent. If there is no
+ * {@link android.content.ContentProvider} associated with the given intent this will throw
+ * {@link IllegalArgumentException}.
+ *
+ * @param intent The intent associated with a slice.
+ * @param supportedSpecs List of supported specs.
+ * @return The Slice provided by the app or null if none is given.
+ * @see Slice
+ * @see SliceProvider#onMapIntentToUri(Intent)
+ * @see Intent
+ */
+ public @Nullable Slice bindSlice(@NonNull Intent intent,
+ @NonNull List<SliceSpec> supportedSpecs) {
+ Preconditions.checkNotNull(intent, "intent");
+ Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null,
+ "Slice intent must be explicit " + intent);
+ ContentResolver resolver = mContext.getContentResolver();
+
+ // Check if the intent has data for the slice uri on it and use that
+ final Uri intentData = intent.getData();
+ if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
+ return bindSlice(intentData, supportedSpecs);
+ }
+ // Otherwise ask the app
+ List<ResolveInfo> providers =
+ mContext.getPackageManager().queryIntentContentProviders(intent, 0);
+ if (providers == null) {
+ throw new IllegalArgumentException("Unable to resolve intent " + intent);
+ }
+ String authority = providers.get(0).providerInfo.authority;
+ Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority).build();
+ IContentProvider provider = resolver.acquireProvider(uri);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ try {
+ Bundle extras = new Bundle();
+ extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
+ extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+ new ArrayList<>(supportedSpecs));
+ final Bundle res = provider.call(resolver.getPackageName(),
+ SliceProvider.METHOD_MAP_INTENT, null, extras);
+ if (res == null) {
+ return null;
+ }
+ return res.getParcelable(SliceProvider.EXTRA_SLICE);
+ } catch (RemoteException e) {
+ // Arbitrary and not worth documenting, as Activity
+ // Manager will kill this process shortly anyway.
+ return null;
+ } finally {
+ resolver.releaseProvider(provider);
+ }
+ }
+
+ /**
* Class that listens to changes in {@link Slice}s.
*/
public interface SliceCallback {
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index 8483931..aa41f14 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -36,6 +36,9 @@
import android.os.UserHandle;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -113,11 +116,19 @@
/**
* @hide
*/
+ public static final String METHOD_GET_DESCENDANTS = "get_descendants";
+ /**
+ * @hide
+ */
public static final String EXTRA_INTENT = "slice_intent";
/**
* @hide
*/
public static final String EXTRA_SLICE = "slice";
+ /**
+ * @hide
+ */
+ public static final String EXTRA_SLICE_DESCENDANTS = "slice_descendants";
private static final boolean DEBUG = false;
@@ -139,14 +150,6 @@
* @see {@link Slice#HINT_PARTIAL}
*/
public Slice onBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
- return onBindSlice(sliceUri);
- }
-
- /**
- * @deprecated migrating to {@link #onBindSlice(Uri, List)}
- */
- @Deprecated
- public Slice onBindSlice(Uri sliceUri) {
return null;
}
@@ -183,6 +186,20 @@
}
/**
+ * Obtains a list of slices that are descendants of the specified Uri.
+ * <p>
+ * Implementing this is optional for a SliceProvider, but does provide a good
+ * discovery mechanism for finding slice Uris.
+ *
+ * @param uri The uri to look for descendants under.
+ * @return All slices within the space.
+ * @see SliceManager#getSliceDescendants(Uri)
+ */
+ public @NonNull Collection<Uri> onGetSliceDescendants(@NonNull Uri uri) {
+ return Collections.emptyList();
+ }
+
+ /**
* This method must be overridden if an {@link IntentFilter} is specified on the SliceProvider.
* In that case, this method can be called and is expected to return a non-null Uri representing
* a slice. Otherwise this will throw {@link UnsupportedOperationException}.
@@ -290,10 +307,35 @@
"Slice binding requires the permission BIND_SLICE");
}
handleUnpinSlice(uri);
+ } else if (method.equals(METHOD_GET_DESCENDANTS)) {
+ Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+ Bundle b = new Bundle();
+ b.putParcelableArrayList(EXTRA_SLICE_DESCENDANTS,
+ new ArrayList<>(handleGetDescendants(uri)));
+ return b;
}
return super.call(method, arg, extras);
}
+ private Collection<Uri> handleGetDescendants(Uri uri) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ return onGetSliceDescendants(uri);
+ } else {
+ CountDownLatch latch = new CountDownLatch(1);
+ Collection<Uri>[] output = new Collection[1];
+ Handler.getMain().post(() -> {
+ output[0] = onGetSliceDescendants(uri);
+ latch.countDown();
+ });
+ try {
+ latch.await();
+ return output[0];
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
private void handlePinSlice(Uri sliceUri) {
if (Looper.myLooper() == Looper.getMainLooper()) {
onSlicePinned(sliceUri);
diff --git a/core/java/android/app/trust/ITrustListener.aidl b/core/java/android/app/trust/ITrustListener.aidl
index 506dd12..65b0249 100644
--- a/core/java/android/app/trust/ITrustListener.aidl
+++ b/core/java/android/app/trust/ITrustListener.aidl
@@ -24,4 +24,5 @@
oneway interface ITrustListener {
void onTrustChanged(boolean enabled, int userId, int flags);
void onTrustManagedChanged(boolean managed, int userId);
+ void onTrustError(in CharSequence message);
}
\ No newline at end of file
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 852cb8e..8ab0b70 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -36,9 +36,11 @@
private static final int MSG_TRUST_CHANGED = 1;
private static final int MSG_TRUST_MANAGED_CHANGED = 2;
+ private static final int MSG_TRUST_ERROR = 3;
private static final String TAG = "TrustManager";
private static final String DATA_FLAGS = "initiatedByUser";
+ private static final String DATA_MESSAGE = "message";
private final ITrustManager mService;
private final ArrayMap<TrustListener, ITrustListener> mTrustListeners;
@@ -148,6 +150,13 @@
mHandler.obtainMessage(MSG_TRUST_MANAGED_CHANGED, (managed ? 1 : 0), userId,
trustListener).sendToTarget();
}
+
+ @Override
+ public void onTrustError(CharSequence message) {
+ Message m = mHandler.obtainMessage(MSG_TRUST_ERROR);
+ m.getData().putCharSequence(DATA_MESSAGE, message);
+ m.sendToTarget();
+ }
};
mService.registerTrustListener(iTrustListener);
mTrustListeners.put(trustListener, iTrustListener);
@@ -221,6 +230,10 @@
break;
case MSG_TRUST_MANAGED_CHANGED:
((TrustListener)msg.obj).onTrustManagedChanged(msg.arg1 != 0, msg.arg2);
+ break;
+ case MSG_TRUST_ERROR:
+ final CharSequence message = msg.peekData().getCharSequence(DATA_MESSAGE);
+ ((TrustListener)msg.obj).onTrustError(message);
}
}
};
@@ -229,9 +242,9 @@
/**
* Reports that the trust state has changed.
- * @param enabled if true, the system believes the environment to be trusted.
- * @param userId the user, for which the trust changed.
- * @param flags flags specified by the trust agent when granting trust. See
+ * @param enabled If true, the system believes the environment to be trusted.
+ * @param userId The user, for which the trust changed.
+ * @param flags Flags specified by the trust agent when granting trust. See
* {@link android.service.trust.TrustAgentService#grantTrust(CharSequence, long, int)
* TrustAgentService.grantTrust(CharSequence, long, int)}.
*/
@@ -239,9 +252,15 @@
/**
* Reports that whether trust is managed has changed
- * @param enabled if true, at least one trust agent is managing trust.
- * @param userId the user, for which the state changed.
+ * @param enabled If true, at least one trust agent is managing trust.
+ * @param userId The user, for which the state changed.
*/
void onTrustManagedChanged(boolean enabled, int userId);
+
+ /**
+ * Reports that an error happened on a TrustAgentService.
+ * @param message A message that should be displayed on the UI.
+ */
+ void onTrustError(CharSequence message);
}
}
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 93e14de..5cd0981 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -22,6 +22,7 @@
import android.content.res.Configuration;
import java.util.List;
+import java.util.Set;
/**
* UsageStatsManager local system service interface.
@@ -159,6 +160,22 @@
public abstract void applyRestoredPayload(@UserIdInt int userId, String key, byte[] payload);
/**
+ * Called by DevicePolicyManagerService to inform that a new admin has been added.
+ *
+ * @param packageName the package in which the admin component is part of.
+ * @param userId the userId in which the admin has been added.
+ */
+ public abstract void onActiveAdminAdded(String packageName, int userId);
+
+ /**
+ * Called by DevicePolicyManagerService to inform about the active admins in an user.
+ *
+ * @param adminApps the set of active admins in {@param userId} or null if there are none.
+ * @param userId the userId to which the admin apps belong.
+ */
+ public abstract void setActiveAdminApps(Set<String> adminApps, int userId);
+
+ /**
* Return usage stats.
*
* @param obfuscateInstantApps whether instant app package names need to be obfuscated in the
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 3290d57..db566e8 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1675,6 +1675,27 @@
}
/**
+ * Get the maximum number of connected audio devices.
+ *
+ * @return the maximum number of connected audio devices
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public int getMaxConnectedAudioDevices() {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ return mService.getMaxConnectedAudioDevices();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to get getMaxConnectedAudioDevices, error: ", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return 1;
+ }
+
+ /**
* Return true if hardware has entries available for matching beacons
*
* @return true if there are hw entries available for matching beacons
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index ad7a93c..9b736b7 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -618,6 +618,7 @@
*
* @hide
*/
+ @SystemApi
public static final int ACCESS_UNKNOWN = 0;
/**
@@ -626,6 +627,7 @@
*
* @hide
*/
+ @SystemApi
public static final int ACCESS_ALLOWED = 1;
/**
@@ -634,6 +636,7 @@
*
* @hide
*/
+ @SystemApi
public static final int ACCESS_REJECTED = 2;
/**
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index c94540a..a68f485 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -556,8 +556,8 @@
* Set priority of the profile
*
* <p> The device should already be paired.
- * Priority can be one of {@link #PRIORITY_ON} or
- * {@link #PRIORITY_OFF},
+ * Priority can be one of {@link BluetoothProfile#PRIORITY_ON} or
+ * {@link BluetoothProfile#PRIORITY_OFF},
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
* permission.
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
index dc00d63..d46b2e3 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
@@ -73,17 +73,18 @@
private final boolean mOutgoing;
private final UUID mUUID;
private final long mCreationElapsedMilli;
+ private final boolean mInBandRing;
/**
* Creates BluetoothHeadsetClientCall instance.
*/
public BluetoothHeadsetClientCall(BluetoothDevice device, int id, int state, String number,
- boolean multiParty, boolean outgoing) {
- this(device, id, UUID.randomUUID(), state, number, multiParty, outgoing);
+ boolean multiParty, boolean outgoing, boolean inBandRing) {
+ this(device, id, UUID.randomUUID(), state, number, multiParty, outgoing, inBandRing);
}
public BluetoothHeadsetClientCall(BluetoothDevice device, int id, UUID uuid, int state,
- String number, boolean multiParty, boolean outgoing) {
+ String number, boolean multiParty, boolean outgoing, boolean inBandRing) {
mDevice = device;
mId = id;
mUUID = uuid;
@@ -91,6 +92,7 @@
mNumber = number != null ? number : "";
mMultiParty = multiParty;
mOutgoing = outgoing;
+ mInBandRing = inBandRing;
mCreationElapsedMilli = SystemClock.elapsedRealtime();
}
@@ -200,6 +202,16 @@
return mOutgoing;
}
+ /**
+ * Checks if the ringtone will be generated by the connected phone
+ *
+ * @return <code>true</code> if in band ring is enabled, <code>false</code> otherwise.
+ */
+ public boolean isInBandRing() {
+ return mInBandRing;
+ }
+
+
@Override
public String toString() {
return toString(false);
@@ -253,6 +265,8 @@
builder.append(mMultiParty);
builder.append(", mOutgoing: ");
builder.append(mOutgoing);
+ builder.append(", mInBandRing: ");
+ builder.append(mInBandRing);
builder.append("}");
return builder.toString();
}
@@ -266,7 +280,8 @@
public BluetoothHeadsetClientCall createFromParcel(Parcel in) {
return new BluetoothHeadsetClientCall((BluetoothDevice) in.readParcelable(null),
in.readInt(), UUID.fromString(in.readString()), in.readInt(),
- in.readString(), in.readInt() == 1, in.readInt() == 1);
+ in.readString(), in.readInt() == 1, in.readInt() == 1,
+ in.readInt() == 1);
}
@Override
@@ -284,6 +299,7 @@
out.writeString(mNumber);
out.writeInt(mMultiParty ? 1 : 0);
out.writeInt(mOutgoing ? 1 : 0);
+ out.writeInt(mInBandRing ? 1 : 0);
}
@Override
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 41cf809..0e2263f 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -19,6 +19,7 @@
import android.Manifest;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import java.util.List;
@@ -185,6 +186,7 @@
*
* @hide
**/
+ @SystemApi
public static final int PRIORITY_ON = 100;
/**
@@ -193,6 +195,7 @@
*
* @hide
**/
+ @SystemApi
public static final int PRIORITY_OFF = 0;
/**
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 06f9171..f69e764 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3223,7 +3223,7 @@
public abstract @Nullable String getSystemServiceName(@NonNull Class<?> serviceClass);
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.os.PowerManager} for controlling power management,
* including "wake locks," which let you keep the device on while
* you're running long tasks.
@@ -3231,117 +3231,117 @@
public static final String POWER_SERVICE = "power";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.os.RecoverySystem} for accessing the recovery system
* service.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @hide
*/
public static final String RECOVERY_SERVICE = "recovery";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.view.WindowManager} for accessing the system's window
* manager.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.view.WindowManager
*/
public static final String WINDOW_SERVICE = "window";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.view.LayoutInflater} for inflating layout resources in this
* context.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.view.LayoutInflater
*/
public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.accounts.AccountManager} for receiving intents at a
* time of your choosing.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.accounts.AccountManager
*/
public static final String ACCOUNT_SERVICE = "account";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.ActivityManager} for interacting with the global
* system state.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.ActivityManager
*/
public static final String ACTIVITY_SERVICE = "activity";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.AlarmManager} for receiving intents at a
* time of your choosing.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.AlarmManager
*/
public static final String ALARM_SERVICE = "alarm";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.NotificationManager} for informing the user of
* background events.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.NotificationManager
*/
public static final String NOTIFICATION_SERVICE = "notification";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.view.accessibility.AccessibilityManager} for giving the user
* feedback for UI events through the registered event listeners.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.view.accessibility.AccessibilityManager
*/
public static final String ACCESSIBILITY_SERVICE = "accessibility";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.view.accessibility.CaptioningManager} for obtaining
* captioning properties and listening for changes in captioning
* preferences.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.view.accessibility.CaptioningManager
*/
public static final String CAPTIONING_SERVICE = "captioning";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.NotificationManager} for controlling keyguard.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.KeyguardManager
*/
public static final String KEYGUARD_SERVICE = "keyguard";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.location.LocationManager} for controlling location
* updates.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.location.LocationManager
*/
public static final String LOCATION_SERVICE = "location";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.location.CountryDetector} for detecting the country that
* the user is in.
*
@@ -3350,96 +3350,96 @@
public static final String COUNTRY_DETECTOR = "country_detector";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.app.SearchManager} for handling searches.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.SearchManager
*/
public static final String SEARCH_SERVICE = "search";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.hardware.SensorManager} for accessing sensors.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.hardware.SensorManager
*/
public static final String SENSOR_SERVICE = "sensor";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.os.storage.StorageManager} for accessing system storage
* functions.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.os.storage.StorageManager
*/
public static final String STORAGE_SERVICE = "storage";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.app.usage.StorageStatsManager} for accessing system storage
* statistics.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.usage.StorageStatsManager
*/
public static final String STORAGE_STATS_SERVICE = "storagestats";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* com.android.server.WallpaperService for accessing wallpapers.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String WALLPAPER_SERVICE = "wallpaper";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.os.Vibrator} for interacting with the vibration hardware.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.os.Vibrator
*/
public static final String VIBRATOR_SERVICE = "vibrator";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.app.StatusBarManager} for interacting with the status bar.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.StatusBarManager
* @hide
*/
public static final String STATUS_BAR_SERVICE = "statusbar";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.ConnectivityManager} for handling management of
* network connections.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.net.ConnectivityManager
*/
public static final String CONNECTIVITY_SERVICE = "connectivity";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.net.IpSecManager} for encrypting Sockets or Networks with
* IPSec.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String IPSEC_SERVICE = "ipsec";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.os.IUpdateLock} for managing runtime sequences that
* must not be interrupted by headless OTA application or similar.
*
* @hide
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.os.UpdateLock
*/
public static final String UPDATE_LOCK_SERVICE = "updatelock";
@@ -3451,18 +3451,18 @@
public static final String NETWORKMANAGEMENT_SERVICE = "network_management";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link com.android.server.slice.SliceManagerService} for managing slices.
* @hide
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String SLICE_SERVICE = "slice";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.app.usage.NetworkStatsManager} for querying network usage stats.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.usage.NetworkStatsManager
*/
public static final String NETWORK_STATS_SERVICE = "netstats";
@@ -3472,40 +3472,40 @@
public static final String NETWORK_WATCHLIST_SERVICE = "network_watchlist";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.wifi.WifiManager} for handling management of
* Wi-Fi access.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.net.wifi.WifiManager
*/
public static final String WIFI_SERVICE = "wifi";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.wifi.p2p.WifiP2pManager} for handling management of
* Wi-Fi peer-to-peer connections.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.net.wifi.p2p.WifiP2pManager
*/
public static final String WIFI_P2P_SERVICE = "wifip2p";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.net.wifi.aware.WifiAwareManager} for handling management of
* Wi-Fi Aware.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.net.wifi.aware.WifiAwareManager
*/
public static final String WIFI_AWARE_SERVICE = "wifiaware";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.wifi.WifiScanner} for scanning the wifi universe
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.net.wifi.WifiScanner
* @hide
*/
@@ -3513,10 +3513,10 @@
public static final String WIFI_SCANNING_SERVICE = "wifiscanner";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.wifi.RttManager} for ranging devices with wifi
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.net.wifi.RttManager
* @hide
*/
@@ -3524,23 +3524,23 @@
public static final String WIFI_RTT_SERVICE = "rttmanager";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.wifi.rtt.WifiRttManager} for ranging devices with wifi
*
* Note: this is a replacement for WIFI_RTT_SERVICE above. It will
* be renamed once final implementation in place.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.net.wifi.rtt.WifiRttManager
*/
public static final String WIFI_RTT_RANGING_SERVICE = "wifirtt";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.lowpan.LowpanManager} for handling management of
* LoWPAN access.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.net.lowpan.LowpanManager
*
* @hide
@@ -3548,11 +3548,11 @@
public static final String LOWPAN_SERVICE = "lowpan";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.EthernetManager} for handling management of
* Ethernet access.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.net.EthernetManager
*
* @hide
@@ -3560,98 +3560,98 @@
public static final String ETHERNET_SERVICE = "ethernet";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.nsd.NsdManager} for handling management of network service
* discovery
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.net.nsd.NsdManager
*/
public static final String NSD_SERVICE = "servicediscovery";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.media.AudioManager} for handling management of volume,
* ringer modes and audio routing.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.media.AudioManager
*/
public static final String AUDIO_SERVICE = "audio";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.hardware.fingerprint.FingerprintManager} for handling management
* of fingerprints.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.hardware.fingerprint.FingerprintManager
*/
public static final String FINGERPRINT_SERVICE = "fingerprint";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.media.MediaRouter} for controlling and managing
* routing of media.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.media.MediaRouter
*/
public static final String MEDIA_ROUTER_SERVICE = "media_router";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.media.session.MediaSessionManager} for managing media Sessions.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.media.session.MediaSessionManager
*/
public static final String MEDIA_SESSION_SERVICE = "media_session";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.telephony.TelephonyManager} for handling management the
* telephony features of the device.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.telephony.TelephonyManager
*/
public static final String TELEPHONY_SERVICE = "phone";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.telephony.SubscriptionManager} for handling management the
* telephony subscriptions of the device.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.telephony.SubscriptionManager
*/
public static final String TELEPHONY_SUBSCRIPTION_SERVICE = "telephony_subscription_service";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.telecom.TelecomManager} to manage telecom-related features
* of the device.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.telecom.TelecomManager
*/
public static final String TELECOM_SERVICE = "telecom";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.telephony.CarrierConfigManager} for reading carrier configuration values.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.telephony.CarrierConfigManager
*/
public static final String CARRIER_CONFIG_SERVICE = "carrier_config";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.telephony.euicc.EuiccManager} to manage the device eUICC (embedded SIM).
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.telephony.euicc.EuiccManager
* TODO(b/35851809): Unhide this API.
* @hide
@@ -3659,47 +3659,58 @@
public static final String EUICC_SERVICE = "euicc_service";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.telephony.euicc.EuiccCardManager} to access the device eUICC (embedded SIM).
+ *
+ * @see #getSystemService(String)
+ * @see android.telephony.euicc.EuiccCardManager
+ * TODO(b/35851809): Make this a SystemApi.
+ * @hide
+ */
+ public static final String EUICC_CARD_SERVICE = "euicc_card_service";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.content.ClipboardManager} for accessing and modifying
* the contents of the global clipboard.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.content.ClipboardManager
*/
public static final String CLIPBOARD_SERVICE = "clipboard";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link TextClassificationManager} for text classification services.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see TextClassificationManager
*/
public static final String TEXT_CLASSIFICATION_SERVICE = "textclassification";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.view.inputmethod.InputMethodManager} for accessing input
* methods.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String INPUT_METHOD_SERVICE = "input_method";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.view.textservice.TextServicesManager} for accessing
* text services.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.appwidget.AppWidgetManager} for accessing AppWidgets.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String APPWIDGET_SERVICE = "appwidget";
@@ -3707,7 +3718,7 @@
* Official published name of the (internal) voice interaction manager service.
*
* @hide
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String VOICE_INTERACTION_MANAGER_SERVICE = "voiceinteraction";
@@ -3715,119 +3726,119 @@
* Official published name of the (internal) autofill service.
*
* @hide
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String AUTOFILL_MANAGER_SERVICE = "autofill";
/**
- * Use with {@link #getSystemService} to access the
+ * Use with {@link #getSystemService(String)} to access the
* {@link com.android.server.voiceinteraction.SoundTriggerService}.
*
* @hide
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String SOUND_TRIGGER_SERVICE = "soundtrigger";
/**
- * Use with {@link #getSystemService} to retrieve an
+ * Use with {@link #getSystemService(String)} to retrieve an
* {@link android.app.backup.IBackupManager IBackupManager} for communicating
* with the backup mechanism.
* @hide
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
@SystemApi
public static final String BACKUP_SERVICE = "backup";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.os.DropBoxManager} instance for recording
* diagnostic logs.
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String DROPBOX_SERVICE = "dropbox";
/**
* System service name for the DeviceIdleController. There is no Java API for this.
- * @see #getSystemService
+ * @see #getSystemService(String)
* @hide
*/
public static final String DEVICE_IDLE_CONTROLLER = "deviceidle";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.admin.DevicePolicyManager} for working with global
* device policy management.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String DEVICE_POLICY_SERVICE = "device_policy";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.UiModeManager} for controlling UI modes.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String UI_MODE_SERVICE = "uimode";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.DownloadManager} for requesting HTTP downloads.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String DOWNLOAD_SERVICE = "download";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.os.BatteryManager} for managing battery state.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String BATTERY_SERVICE = "batterymanager";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.nfc.NfcManager} for using NFC.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String NFC_SERVICE = "nfc";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.bluetooth.BluetoothManager} for using Bluetooth.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String BLUETOOTH_SERVICE = "bluetooth";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.net.sip.SipManager} for accessing the SIP related service.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
/** @hide */
public static final String SIP_SERVICE = "sip";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.hardware.usb.UsbManager} for access to USB devices (as a USB host)
* and for controlling this device's behavior as a USB device.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.hardware.usb.UsbManager
*/
public static final String USB_SERVICE = "usb";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.hardware.SerialManager} for access to serial ports.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.hardware.SerialManager
*
* @hide
@@ -3835,11 +3846,11 @@
public static final String SERIAL_SERVICE = "serial";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.hardware.hdmi.HdmiControlManager} for controlling and managing
* HDMI-CEC protocol.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.hardware.hdmi.HdmiControlManager
* @hide
*/
@@ -3847,67 +3858,67 @@
public static final String HDMI_CONTROL_SERVICE = "hdmi_control";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.hardware.input.InputManager} for interacting with input devices.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.hardware.input.InputManager
*/
public static final String INPUT_SERVICE = "input";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.hardware.display.DisplayManager} for interacting with display devices.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.hardware.display.DisplayManager
*/
public static final String DISPLAY_SERVICE = "display";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.os.UserManager} for managing users on devices that support multiple users.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.os.UserManager
*/
public static final String USER_SERVICE = "user";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.content.pm.LauncherApps} for querying and monitoring launchable apps across
* profiles of a user.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.content.pm.LauncherApps
*/
public static final String LAUNCHER_APPS_SERVICE = "launcherapps";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.content.RestrictionsManager} for retrieving application restrictions
* and requesting permissions for restricted operations.
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.content.RestrictionsManager
*/
public static final String RESTRICTIONS_SERVICE = "restrictions";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.AppOpsManager} for tracking application operations
* on the device.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.AppOpsManager
*/
public static final String APP_OPS_SERVICE = "appops";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.hardware.camera2.CameraManager} for interacting with
* camera devices.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.hardware.camera2.CameraManager
*/
public static final String CAMERA_SERVICE = "camera";
@@ -3916,51 +3927,51 @@
* {@link android.print.PrintManager} for printing and managing
* printers and print tasks.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.print.PrintManager
*/
public static final String PRINT_SERVICE = "print";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.companion.CompanionDeviceManager} for managing companion devices
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.companion.CompanionDeviceManager
*/
public static final String COMPANION_DEVICE_SERVICE = "companiondevice";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.hardware.ConsumerIrManager} for transmitting infrared
* signals from the device.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.hardware.ConsumerIrManager
*/
public static final String CONSUMER_IR_SERVICE = "consumer_ir";
/**
* {@link android.app.trust.TrustManager} for managing trust agents.
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.trust.TrustManager
* @hide
*/
public static final String TRUST_SERVICE = "trust";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.media.tv.TvInputManager} for interacting with TV inputs
* on the device.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.media.tv.TvInputManager
*/
public static final String TV_INPUT_SERVICE = "tv_input";
/**
* {@link android.net.NetworkScoreManager} for managing network scoring.
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.net.NetworkScoreManager
* @hide
*/
@@ -3968,29 +3979,29 @@
public static final String NETWORK_SCORE_SERVICE = "network_score";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.app.usage.UsageStatsManager} for querying device usage stats.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.usage.UsageStatsManager
*/
public static final String USAGE_STATS_SERVICE = "usagestats";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.app.job.JobScheduler} instance for managing occasional
* background tasks.
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.app.job.JobScheduler
*/
public static final String JOB_SCHEDULER_SERVICE = "jobscheduler";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.service.persistentdata.PersistentDataBlockManager} instance
* for interacting with a storage device that lives across factory resets.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.service.persistentdata.PersistentDataBlockManager
* @hide
*/
@@ -3998,10 +4009,10 @@
public static final String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.service.oemlock.OemLockManager} instance for managing the OEM lock.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.service.oemlock.OemLockManager
* @hide
*/
@@ -4009,54 +4020,54 @@
public static final String OEM_LOCK_SERVICE = "oem_lock";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.media.projection.MediaProjectionManager} instance for managing
* media projection sessions.
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.media.projection.MediaProjectionManager
*/
public static final String MEDIA_PROJECTION_SERVICE = "media_projection";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.media.midi.MidiManager} for accessing the MIDI service.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String MIDI_SERVICE = "midi";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.hardware.radio.RadioManager} for accessing the broadcast radio service.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @hide
*/
public static final String RADIO_SERVICE = "broadcastradio";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.os.HardwarePropertiesManager} for accessing the hardware properties service.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String HARDWARE_PROPERTIES_SERVICE = "hardware_properties";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.content.pm.ShortcutManager} for accessing the launcher shortcut service.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.content.pm.ShortcutManager
*/
public static final String SHORTCUT_SERVICE = "shortcut";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.hardware.location.ContextHubManager} for accessing context hubs.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.hardware.location.ContextHubManager
*
* @hide
@@ -4065,11 +4076,11 @@
public static final String CONTEXTHUB_SERVICE = "contexthub";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.os.health.SystemHealthManager} for accessing system health (battery, power,
* memory, etc) metrics.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String SYSTEM_HEALTH_SERVICE = "systemhealth";
@@ -4098,46 +4109,46 @@
public static final String STATS_COMPANION_SERVICE = "statscompanion";
/**
- * Use with {@link #getSystemService} to retrieve an {@link android.stats.StatsManager}.
+ * Use with {@link #getSystemService(String)} to retrieve an {@link android.stats.StatsManager}.
* @hide
*/
@SystemApi
public static final String STATS_MANAGER = "stats";
/**
- * Use with {@link #getSystemService} to retrieve a {@link
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.content.om.OverlayManager} for managing overlay packages.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @see android.content.om.OverlayManager
* @hide
*/
public static final String OVERLAY_SERVICE = "overlay";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link VrManager} for accessing the VR service.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
* @hide
*/
@SystemApi
public static final String VR_SERVICE = "vrmanager";
/**
- * Use with {@link #getSystemService} to retrieve an
+ * Use with {@link #getSystemService(String)} to retrieve an
* {@link android.app.timezone.ITimeZoneRulesManager}.
* @hide
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String TIME_ZONE_RULES_MANAGER_SERVICE = "timezone";
/**
- * Use with {@link #getSystemService} to retrieve a
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.content.pm.crossprofile.CrossProfileApps} for cross profile operations.
*
- * @see #getSystemService
+ * @see #getSystemService(String)
*/
public static final String CROSS_PROFILE_APPS_SERVICE = "crossprofileapps";
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index e319750..cce6b84 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -652,4 +652,8 @@
String getInstantAppAndroidId(String packageName, int userId);
IArtManager getArtManager();
+
+ void setHarmfulAppWarning(String packageName, CharSequence warning, int userId);
+
+ CharSequence getHarmfulAppWarning(String packageName, int userId);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 6cd4285..44b4a33 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -787,7 +787,8 @@
/**
* Flag parameter for {@link #installPackage} to indicate that this package is an
- * upgrade to a package that refers to the SDK via release letter.
+ * upgrade to a package that refers to the SDK via release letter or is targeting an SDK via
+ * release letter that the current build does not support.
*
* @hide
*/
@@ -2534,31 +2535,22 @@
* Devices declaring this feature must include an application implementing a
* {@link android.service.vr.VrListenerService} that can be targeted by VR applications via
* {@link android.app.Activity#setVrModeEnabled}.
+ * @deprecated use {@link #FEATURE_VR_MODE_HIGH_PERFORMANCE} instead.
*/
+ @Deprecated
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_VR_MODE = "android.software.vr.mode";
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
- * The device implements {@link #FEATURE_VR_MODE} but additionally meets extra CDD requirements
- * to provide a high-quality VR experience. In general, devices declaring this feature will
- * additionally:
- * <ul>
- * <li>Deliver consistent performance at a high framerate over an extended period of time
- * for typical VR application CPU/GPU workloads with a minimal number of frame drops for VR
- * applications that have called
- * {@link android.view.Window#setSustainedPerformanceMode}.</li>
- * <li>Implement {@link #FEATURE_HIFI_SENSORS} and have a low sensor latency.</li>
- * <li>Include optimizations to lower display persistence while running VR applications.</li>
- * <li>Implement an optimized render path to minimize latency to draw to the device's main
- * display.</li>
- * <li>Include the following EGL extensions: EGL_ANDROID_create_native_client_buffer,
- * EGL_ANDROID_front_buffer_auto_refresh, EGL_EXT_protected_content,
- * EGL_KHR_mutable_render_buffer, EGL_KHR_reusable_sync, and EGL_KHR_wait_sync.</li>
- * <li>Provide at least one CPU core that is reserved for use solely by the top, foreground
- * VR application process for critical render threads while such an application is
- * running.</li>
- * </ul>
+ * The device implements an optimized mode for virtual reality (VR) applications that handles
+ * stereoscopic rendering of notifications, disables most monocular system UI components
+ * while a VR application has user focus and meets extra CDD requirements to provide a
+ * high-quality VR experience.
+ * Devices declaring this feature must include an application implementing a
+ * {@link android.service.vr.VrListenerService} that can be targeted by VR applications via
+ * {@link android.app.Activity#setVrModeEnabled}.
+ * and must meet CDD requirements to provide a high-quality VR experience.
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_VR_MODE_HIGH_PERFORMANCE
@@ -5862,4 +5854,37 @@
public @NonNull ArtManager getArtManager() {
throw new UnsupportedOperationException("getArtManager not implemented in subclass");
}
+
+ /**
+ * Sets or clears the harmful app warning details for the given app.
+ *
+ * When set, any attempt to launch an activity in this package will be intercepted and a
+ * warning dialog will be shown to the user instead, with the given warning. The user
+ * will have the option to proceed with the activity launch, or to uninstall the application.
+ *
+ * @param packageName The full name of the package to warn on.
+ * @param warning A warning string to display to the user describing the threat posed by the
+ * application, or null to clear the warning.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.SET_HARMFUL_APP_WARNINGS)
+ @SystemApi
+ public void setHarmfulAppWarning(@NonNull String packageName, @Nullable CharSequence warning) {
+ throw new UnsupportedOperationException("setHarmfulAppWarning not implemented in subclass");
+ }
+
+ /**
+ * Returns the harmful app warning string for the given app, or null if there is none set.
+ *
+ * @param packageName The full name of the desired package.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.SET_HARMFUL_APP_WARNINGS)
+ @Nullable
+ @SystemApi
+ public CharSequence getHarmfulAppWarning(@NonNull String packageName) {
+ throw new UnsupportedOperationException("getHarmfulAppWarning not implemented in subclass");
+ }
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 77eb57f2..6d6c02a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -35,7 +35,6 @@
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
import static android.os.Build.VERSION_CODES.O;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
@@ -85,7 +84,6 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.TypedValue;
-import android.util.apk.ApkSignatureSchemeV2Verifier;
import android.util.apk.ApkSignatureVerifier;
import android.view.Gravity;
@@ -112,8 +110,7 @@
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
@@ -158,10 +155,6 @@
private static final boolean MULTI_PACKAGE_APK_ENABLED = Build.IS_DEBUGGABLE &&
SystemProperties.getBoolean(PROPERTY_CHILD_PACKAGES_ENABLED, false);
- public static final int APK_SIGNING_UNKNOWN = 0;
- public static final int APK_SIGNING_V1 = 1;
- public static final int APK_SIGNING_V2 = 2;
-
private static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f;
// TODO: switch outError users to PackageParserException
@@ -477,8 +470,7 @@
public final int revisionCode;
public final int installLocation;
public final VerifierInfo[] verifiers;
- public final Signature[] signatures;
- public final Certificate[][] certificates;
+ public final SigningDetails signingDetails;
public final boolean coreApp;
public final boolean debuggable;
public final boolean multiArch;
@@ -486,10 +478,11 @@
public final boolean extractNativeLibs;
public final boolean isolatedSplits;
- public ApkLite(String codePath, String packageName, String splitName, boolean isFeatureSplit,
+ public ApkLite(String codePath, String packageName, String splitName,
+ boolean isFeatureSplit,
String configForSplit, String usesSplitName, int versionCode, int versionCodeMajor,
int revisionCode, int installLocation, List<VerifierInfo> verifiers,
- Signature[] signatures, Certificate[][] certificates, boolean coreApp,
+ SigningDetails signingDetails, boolean coreApp,
boolean debuggable, boolean multiArch, boolean use32bitAbi,
boolean extractNativeLibs, boolean isolatedSplits) {
this.codePath = codePath;
@@ -502,9 +495,8 @@
this.versionCodeMajor = versionCodeMajor;
this.revisionCode = revisionCode;
this.installLocation = installLocation;
+ this.signingDetails = signingDetails;
this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
- this.signatures = signatures;
- this.certificates = certificates;
this.coreApp = coreApp;
this.debuggable = debuggable;
this.multiArch = multiArch;
@@ -807,10 +799,10 @@
}
}
if ((flags&PackageManager.GET_SIGNATURES) != 0) {
- int N = (p.mSignatures != null) ? p.mSignatures.length : 0;
- if (N > 0) {
- pi.signatures = new Signature[N];
- System.arraycopy(p.mSignatures, 0, pi.signatures, 0, N);
+ if (p.mSigningDetails.hasSignatures()) {
+ int numberOfSigs = p.mSigningDetails.signatures.length;
+ pi.signatures = new Signature[numberOfSigs];
+ System.arraycopy(p.mSigningDetails.signatures, 0, pi.signatures, 0, numberOfSigs);
}
}
return pi;
@@ -818,15 +810,14 @@
public static final int PARSE_MUST_BE_APK = 1 << 0;
public static final int PARSE_IGNORE_PROCESSES = 1 << 1;
+ /** @deprecated forward lock no longer functional. remove. */
+ @Deprecated
public static final int PARSE_FORWARD_LOCK = 1 << 2;
public static final int PARSE_EXTERNAL_STORAGE = 1 << 3;
public static final int PARSE_IS_SYSTEM_DIR = 1 << 4;
public static final int PARSE_COLLECT_CERTIFICATES = 1 << 5;
public static final int PARSE_ENFORCE_CODE = 1 << 6;
public static final int PARSE_FORCE_SDK = 1 << 7;
- /** @deprecated remove when fixing b/68860689 */
- @Deprecated
- public static final int PARSE_IS_EPHEMERAL = 1 << 8;
public static final int PARSE_CHATTY = 1 << 31;
@IntDef(flag = true, prefix = { "PARSE_" }, value = {
@@ -837,7 +828,6 @@
PARSE_FORCE_SDK,
PARSE_FORWARD_LOCK,
PARSE_IGNORE_PROCESSES,
- PARSE_IS_EPHEMERAL,
PARSE_IS_SYSTEM_DIR,
PARSE_MUST_BE_APK,
})
@@ -1349,7 +1339,7 @@
pkg.setVolumeUuid(volumeUuid);
pkg.setApplicationVolumeUuid(volumeUuid);
pkg.setBaseCodePath(apkPath);
- pkg.setSignatures(null);
+ pkg.setSigningDetails(SigningDetails.UNKNOWN);
return pkg;
@@ -1469,57 +1459,19 @@
return pkg;
}
- public static int getApkSigningVersion(Package pkg) {
- try {
- if (ApkSignatureSchemeV2Verifier.hasSignature(pkg.baseCodePath)) {
- return APK_SIGNING_V2;
- }
- return APK_SIGNING_V1;
- } catch (IOException e) {
+ /** Parses the public keys from the set of signatures. */
+ public static ArraySet<PublicKey> toSigningKeys(Signature[] signatures)
+ throws CertificateException {
+ ArraySet<PublicKey> keys = new ArraySet<>(signatures.length);
+ for (int i = 0; i < signatures.length; i++) {
+ keys.add(signatures[i].getPublicKey());
}
- return APK_SIGNING_UNKNOWN;
- }
-
- /**
- * Populates the correct packages fields with the given certificates.
- * <p>
- * This is useful when we've already processed the certificates [such as during package
- * installation through an installer session]. We don't re-process the archive and
- * simply populate the correct fields.
- */
- public static void populateCertificates(Package pkg, Certificate[][] certificates)
- throws PackageParserException {
- pkg.mCertificates = null;
- pkg.mSignatures = null;
- pkg.mSigningKeys = null;
-
- pkg.mCertificates = certificates;
- try {
- pkg.mSignatures = ApkSignatureVerifier.convertToSignatures(certificates);
- } catch (CertificateEncodingException e) {
- // certificates weren't encoded properly; something went wrong
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "Failed to collect certificates from " + pkg.baseCodePath, e);
- }
- pkg.mSigningKeys = new ArraySet<>(certificates.length);
- for (int i = 0; i < certificates.length; i++) {
- Certificate[] signerCerts = certificates[i];
- Certificate signerCert = signerCerts[0];
- pkg.mSigningKeys.add(signerCert.getPublicKey());
- }
- // add signatures to child packages
- final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
- for (int i = 0; i < childCount; i++) {
- Package childPkg = pkg.childPackages.get(i);
- childPkg.mCertificates = pkg.mCertificates;
- childPkg.mSignatures = pkg.mSignatures;
- childPkg.mSigningKeys = pkg.mSigningKeys;
- }
+ return keys;
}
/**
* Collect certificates from all the APKs described in the given package,
- * populating {@link Package#mSignatures}. Also asserts that all APK
+ * populating {@link Package#mSigningDetails}. Also asserts that all APK
* contents are signed correctly and consistently.
*/
public static void collectCertificates(Package pkg, @ParseFlags int parseFlags)
@@ -1528,17 +1480,13 @@
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
Package childPkg = pkg.childPackages.get(i);
- childPkg.mCertificates = pkg.mCertificates;
- childPkg.mSignatures = pkg.mSignatures;
- childPkg.mSigningKeys = pkg.mSigningKeys;
+ childPkg.mSigningDetails = pkg.mSigningDetails;
}
}
private static void collectCertificatesInternal(Package pkg, @ParseFlags int parseFlags)
throws PackageParserException {
- pkg.mCertificates = null;
- pkg.mSignatures = null;
- pkg.mSigningKeys = null;
+ pkg.mSigningDetails = SigningDetails.UNKNOWN;
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
@@ -1558,12 +1506,12 @@
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
- int minSignatureScheme = ApkSignatureVerifier.VERSION_JAR_SIGNATURE_SCHEME;
+ int minSignatureScheme = SigningDetails.SignatureSchemeVersion.JAR;
if (pkg.applicationInfo.isStaticSharedLibrary()) {
// must use v2 signing scheme
- minSignatureScheme = ApkSignatureVerifier.VERSION_APK_SIGNATURE_SCHEME_V2;
+ minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
}
- ApkSignatureVerifier.Result verified;
+ SigningDetails verified;
if ((parseFlags & PARSE_IS_SYSTEM_DIR) != 0) {
// systemDir APKs are already trusted, save time by not verifying
verified = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts(
@@ -1571,29 +1519,14 @@
} else {
verified = ApkSignatureVerifier.verify(apkPath, minSignatureScheme);
}
- if (verified.signatureSchemeVersion
- < ApkSignatureVerifier.VERSION_APK_SIGNATURE_SCHEME_V2) {
- // TODO (b/68860689): move this logic to packagemanagerserivce
- if ((parseFlags & PARSE_IS_EPHEMERAL) != 0) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "No APK Signature Scheme v2 signature in ephemeral package " + apkPath);
- }
- }
// Verify that entries are signed consistently with the first pkg
// we encountered. Note that for splits, certificates may have
// already been populated during an earlier parse of a base APK.
- if (pkg.mCertificates == null) {
- pkg.mCertificates = verified.certs;
- pkg.mSignatures = verified.sigs;
- pkg.mSigningKeys = new ArraySet<>(verified.certs.length);
- for (int i = 0; i < verified.certs.length; i++) {
- Certificate[] signerCerts = verified.certs[i];
- Certificate signerCert = signerCerts[0];
- pkg.mSigningKeys.add(signerCert.getPublicKey());
- }
+ if (pkg.mSigningDetails == SigningDetails.UNKNOWN) {
+ pkg.mSigningDetails = verified;
} else {
- if (!Signature.areExactMatch(pkg.mSignatures, verified.sigs)) {
+ if (!Signature.areExactMatch(pkg.mSigningDetails.signatures, verified.signatures)) {
throw new PackageParserException(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
apkPath + " has mismatched certificates");
@@ -1655,8 +1588,7 @@
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
- final Signature[] signatures;
- final Certificate[][] certificates;
+ final SigningDetails signingDetails;
if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
// TODO: factor signature related items out of Package object
final Package tempPkg = new Package((String) null);
@@ -1666,15 +1598,13 @@
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- signatures = tempPkg.mSignatures;
- certificates = tempPkg.mCertificates;
+ signingDetails = tempPkg.mSigningDetails;
} else {
- signatures = null;
- certificates = null;
+ signingDetails = SigningDetails.UNKNOWN;
}
final AttributeSet attrs = parser;
- return parseApkLite(apkPath, parser, attrs, signatures, certificates);
+ return parseApkLite(apkPath, parser, attrs, signingDetails);
} catch (XmlPullParserException | IOException | RuntimeException e) {
Slog.w(TAG, "Failed to parse " + apkPath, e);
@@ -1761,7 +1691,7 @@
}
private static ApkLite parseApkLite(String codePath, XmlPullParser parser, AttributeSet attrs,
- Signature[] signatures, Certificate[][] certificates)
+ SigningDetails signingDetails)
throws IOException, XmlPullParserException, PackageParserException {
final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs);
@@ -1854,7 +1784,7 @@
return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
configForSplit, usesSplitName, versionCode, versionCodeMajor, revisionCode,
- installLocation, verifiers, signatures, certificates, coreApp, debuggable,
+ installLocation, verifiers, signingDetails, coreApp, debuggable,
multiArch, use32bitAbi, extractNativeLibs, isolatedSplits);
}
@@ -2039,11 +1969,6 @@
String str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
if (str != null && str.length() > 0) {
- if ((flags & PARSE_IS_EPHEMERAL) != 0) {
- outError[0] = "sharedUserId not allowed in ephemeral application";
- mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
- return null;
- }
String nameError = validateName(str, true, false);
if (nameError != null && !"android".equals(pkg.packageName)) {
outError[0] = "<manifest> specifies bad sharedUserId name \""
@@ -2304,8 +2229,9 @@
return null;
}
+ boolean defaultToCurrentDevBranch = (flags & PARSE_FORCE_SDK) != 0;
final int targetSdkVersion = PackageParser.computeTargetSdkVersion(targetVers,
- targetCode, SDK_VERSION, SDK_CODENAMES, outError);
+ targetCode, SDK_CODENAMES, outError, defaultToCurrentDevBranch);
if (targetSdkVersion < 0) {
mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
return null;
@@ -2621,19 +2547,19 @@
* application manifest, or 0 otherwise
* @param targetCode targetSdkVersion code, if specified in the application
* manifest, or {@code null} otherwise
- * @param platformSdkVersion platform SDK version number, typically
- * Build.VERSION.SDK_INT
* @param platformSdkCodenames array of allowed pre-release SDK codenames
* for this platform
* @param outError output array to populate with error, if applicable
+ * @param forceCurrentDev if development target code is not available, use the current
+ * development version by default.
* @return the targetSdkVersion to use at runtime, or -1 if the package is
* not compatible with this platform
* @hide Exposed for unit testing only.
*/
@TestApi
public static int computeTargetSdkVersion(@IntRange(from = 0) int targetVers,
- @Nullable String targetCode, @IntRange(from = 1) int platformSdkVersion,
- @NonNull String[] platformSdkCodenames, @NonNull String[] outError) {
+ @Nullable String targetCode, @NonNull String[] platformSdkCodenames,
+ @NonNull String[] outError, boolean forceCurrentDev) {
// If it's a release SDK, return the version number unmodified.
if (targetCode == null) {
return targetVers;
@@ -2641,7 +2567,7 @@
// If it's a pre-release SDK and the codename matches this platform, it
// definitely targets this SDK.
- if (ArrayUtils.contains(platformSdkCodenames, targetCode)) {
+ if (ArrayUtils.contains(platformSdkCodenames, targetCode) || forceCurrentDev) {
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
@@ -5734,6 +5660,117 @@
return true;
}
+ /** A container for signing-related data of an application package. */
+ public static final class SigningDetails implements Parcelable {
+
+ @IntDef({SigningDetails.SignatureSchemeVersion.UNKNOWN,
+ SigningDetails.SignatureSchemeVersion.JAR,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3})
+ public @interface SignatureSchemeVersion {
+ int UNKNOWN = 0;
+ int JAR = 1;
+ int SIGNING_BLOCK_V2 = 2;
+ int SIGNING_BLOCK_V3 = 3;
+ }
+
+ @Nullable
+ public final Signature[] signatures;
+ @SignatureSchemeVersion
+ public final int signatureSchemeVersion;
+ @Nullable
+ public final ArraySet<PublicKey> publicKeys;
+
+ /** A representation of unknown signing details. Use instead of null. */
+ public static final SigningDetails UNKNOWN =
+ new SigningDetails(null, SignatureSchemeVersion.UNKNOWN, null);
+
+ @VisibleForTesting
+ public SigningDetails(Signature[] signatures,
+ @SignatureSchemeVersion int signatureSchemeVersion,
+ ArraySet<PublicKey> keys) {
+ this.signatures = signatures;
+ this.signatureSchemeVersion = signatureSchemeVersion;
+ this.publicKeys = keys;
+ }
+
+ public SigningDetails(Signature[] signatures,
+ @SignatureSchemeVersion int signatureSchemeVersion)
+ throws CertificateException {
+ this(signatures, signatureSchemeVersion, toSigningKeys(signatures));
+ }
+
+ /** Returns true if the signing details have one or more signatures. */
+ public boolean hasSignatures() {
+ return signatures != null && signatures.length > 0;
+ }
+
+ /** Returns true if the signatures in this and other match exactly. */
+ public boolean signaturesMatchExactly(SigningDetails other) {
+ return Signature.areExactMatch(this.signatures, other.signatures);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ boolean isUnknown = UNKNOWN == this;
+ dest.writeBoolean(isUnknown);
+ if (isUnknown) {
+ return;
+ }
+ dest.writeTypedArray(this.signatures, flags);
+ dest.writeInt(this.signatureSchemeVersion);
+ dest.writeArraySet(this.publicKeys);
+ }
+
+ protected SigningDetails(Parcel in) {
+ final ClassLoader boot = Object.class.getClassLoader();
+ this.signatures = in.createTypedArray(Signature.CREATOR);
+ this.signatureSchemeVersion = in.readInt();
+ this.publicKeys = (ArraySet<PublicKey>) in.readArraySet(boot);
+ }
+
+ public static final Creator<SigningDetails> CREATOR = new Creator<SigningDetails>() {
+ @Override
+ public SigningDetails createFromParcel(Parcel source) {
+ if (source.readBoolean()) {
+ return UNKNOWN;
+ }
+ return new SigningDetails(source);
+ }
+
+ @Override
+ public SigningDetails[] newArray(int size) {
+ return new SigningDetails[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SigningDetails)) return false;
+
+ SigningDetails that = (SigningDetails) o;
+
+ if (signatureSchemeVersion != that.signatureSchemeVersion) return false;
+ if (!Signature.areExactMatch(signatures, that.signatures)) return false;
+ return publicKeys != null ? publicKeys.equals(that.publicKeys)
+ : that.publicKeys == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = +Arrays.hashCode(signatures);
+ result = 31 * result + signatureSchemeVersion;
+ result = 31 * result + (publicKeys != null ? publicKeys.hashCode() : 0);
+ return result;
+ }
+ }
+
/**
* Representation of a full package parsed from APK files on disk. A package
* consists of a single base APK, and zero or more split APKs.
@@ -5840,8 +5877,7 @@
public int mSharedUserLabel;
// Signatures that were read from the package.
- public Signature[] mSignatures;
- public Certificate[][] mCertificates;
+ @NonNull public SigningDetails mSigningDetails = SigningDetails.UNKNOWN;
// For use by package manager service for quick lookup of
// preferred up order.
@@ -5893,7 +5929,6 @@
/**
* Data used to feed the KeySetManagerService
*/
- public ArraySet<PublicKey> mSigningKeys;
public ArraySet<String> mUpgradeKeySets;
public ArrayMap<String, ArraySet<PublicKey>> mKeySetMapping;
@@ -5950,6 +5985,8 @@
}
}
+ /** @deprecated Forward locked apps no longer supported. Resource path not needed. */
+ @Deprecated
public void setApplicationInfoResourcePath(String resourcePath) {
this.applicationInfo.setResourcePath(resourcePath);
if (childPackages != null) {
@@ -5960,6 +5997,8 @@
}
}
+ /** @deprecated Forward locked apps no longer supported. Resource path not needed. */
+ @Deprecated
public void setApplicationInfoBaseResourcePath(String resourcePath) {
this.applicationInfo.setBaseResourcePath(resourcePath);
if (childPackages != null) {
@@ -6008,6 +6047,8 @@
// Children have no splits
}
+ /** @deprecated Forward locked apps no longer supported. Resource path not needed. */
+ @Deprecated
public void setApplicationInfoSplitResourcePaths(String[] resroucePaths) {
this.applicationInfo.setSplitResourcePaths(resroucePaths);
// Children have no splits
@@ -6037,12 +6078,13 @@
}
}
- public void setSignatures(Signature[] signatures) {
- this.mSignatures = signatures;
+ /** Sets signing details on the package and any of its children. */
+ public void setSigningDetails(@NonNull SigningDetails signingDetails) {
+ mSigningDetails = signingDetails;
if (childPackages != null) {
final int packageCount = childPackages.size();
for (int i = 0; i < packageCount; i++) {
- childPackages.get(i).mSignatures = signatures;
+ childPackages.get(i).mSigningDetails = signingDetails;
}
}
}
@@ -6243,6 +6285,31 @@
+ " " + packageName + "}";
}
+ public String dumpState_temp() {
+ String flags = "";
+ flags += ((applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0 ? "U" : "");
+ flags += ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 ? "S" : "");
+ if ("".equals(flags)) {
+ flags = "-";
+ }
+ String privFlags = "";
+ privFlags += ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0 ? "P" : "");
+ privFlags += ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0 ? "O" : "");
+ privFlags += ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0 ? "V" : "");
+ if ("".equals(privFlags)) {
+ privFlags = "-";
+ }
+ return "Package{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " " + packageName
+ + ", ver:" + getLongVersionCode()
+ + ", path: " + codePath
+ + ", flags: " + flags
+ + ", privFlags: " + privFlags
+ + ", extra: " + (mExtras == null ? "<<NULL>>" : Integer.toHexString(System.identityHashCode(mExtras)) + "}")
+ + "}";
+ }
+
@Override
public int describeContents() {
return 0;
@@ -6348,8 +6415,7 @@
}
mSharedUserLabel = dest.readInt();
- mSignatures = (Signature[]) dest.readParcelableArray(boot, Signature.class);
- mCertificates = (Certificate[][]) dest.readSerializable();
+ mSigningDetails = dest.readParcelable(boot);
mPreferredOrder = dest.readInt();
@@ -6389,7 +6455,6 @@
mTrustedOverlay = (dest.readInt() == 1);
mCompileSdkVersion = dest.readInt();
mCompileSdkVersionCodename = dest.readString();
- mSigningKeys = (ArraySet<PublicKey>) dest.readArraySet(boot);
mUpgradeKeySets = (ArraySet<String>) dest.readArraySet(boot);
mKeySetMapping = readKeySetMapping(dest);
@@ -6489,8 +6554,7 @@
dest.writeString(mSharedUserId);
dest.writeInt(mSharedUserLabel);
- dest.writeParcelableArray(mSignatures, flags);
- dest.writeSerializable(mCertificates);
+ dest.writeParcelable(mSigningDetails, flags);
dest.writeInt(mPreferredOrder);
@@ -6515,7 +6579,6 @@
dest.writeInt(mTrustedOverlay ? 1 : 0);
dest.writeInt(mCompileSdkVersion);
dest.writeString(mCompileSdkVersionCodename);
- dest.writeArraySet(mSigningKeys);
dest.writeArraySet(mUpgradeKeySets);
writeKeySetMapping(dest, mKeySetMapping);
dest.writeString(cpuAbiOverride);
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 069b2d4..293beb2 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -52,6 +52,7 @@
public int appLinkGeneration;
public int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED;
public int installReason;
+ public String harmfulAppWarning;
public ArraySet<String> disabledComponents;
public ArraySet<String> enabledComponents;
@@ -87,6 +88,7 @@
enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents);
overlayPaths =
o.overlayPaths == null ? null : Arrays.copyOf(o.overlayPaths, o.overlayPaths.length);
+ harmfulAppWarning = o.harmfulAppWarning;
}
/**
@@ -247,6 +249,11 @@
}
}
}
+ if (harmfulAppWarning == null && oldState.harmfulAppWarning != null
+ || (harmfulAppWarning != null
+ && !harmfulAppWarning.equals(oldState.harmfulAppWarning))) {
+ return false;
+ }
return true;
}
}
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 3239212..97cb78b 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -815,7 +815,7 @@
} finally {
stack.pop();
}
- } catch (Exception e) {
+ } catch (Exception | StackOverflowError e) {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
final NotFoundException rnf = new NotFoundException(
"File " + file + " from drawable resource ID #0x" + Integer.toHexString(id));
diff --git a/core/java/android/hardware/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java
index 0a08353..2301824 100644
--- a/core/java/android/hardware/display/BrightnessChangeEvent.java
+++ b/core/java/android/hardware/display/BrightnessChangeEvent.java
@@ -16,6 +16,8 @@
package android.hardware.display;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -23,51 +25,65 @@
* Data about a brightness settings change.
*
* {@see DisplayManager.getBrightnessEvents()}
- * TODO make this SystemAPI
* @hide
*/
+@SystemApi
+@TestApi
public final class BrightnessChangeEvent implements Parcelable {
/** Brightness in nits */
- public float brightness;
+ public final float brightness;
/** Timestamp of the change {@see System.currentTimeMillis()} */
- public long timeStamp;
+ public final long timeStamp;
/** Package name of focused activity when brightness was changed.
* This will be null if the caller of {@see DisplayManager.getBrightnessEvents()}
* does not have access to usage stats {@see UsageStatsManager} */
- public String packageName;
+ public final String packageName;
/** User id of of the user running when brightness was changed.
* @hide */
- public int userId;
+ public final int userId;
/** Lux values of recent sensor data */
- public float[] luxValues;
+ public final float[] luxValues;
/** Timestamps of the lux sensor readings {@see System.currentTimeMillis()} */
- public long[] luxTimestamps;
+ public final long[] luxTimestamps;
/** Most recent battery level when brightness was changed or Float.NaN */
- public float batteryLevel;
+ public final float batteryLevel;
/** Color filter active to provide night mode */
- public boolean nightMode;
+ public final boolean nightMode;
/** If night mode color filter is active this will be the temperature in kelvin */
- public int colorTemperature;
+ public final int colorTemperature;
- /** Brightness level before slider adjustment */
- public float lastBrightness;
+ /** Brightness le vel before slider adjustment */
+ public final float lastBrightness;
- public BrightnessChangeEvent() {
+ /** @hide */
+ private BrightnessChangeEvent(float brightness, long timeStamp, String packageName,
+ int userId, float[] luxValues, long[] luxTimestamps, float batteryLevel,
+ boolean nightMode, int colorTemperature, float lastBrightness) {
+ this.brightness = brightness;
+ this.timeStamp = timeStamp;
+ this.packageName = packageName;
+ this.userId = userId;
+ this.luxValues = luxValues;
+ this.luxTimestamps = luxTimestamps;
+ this.batteryLevel = batteryLevel;
+ this.nightMode = nightMode;
+ this.colorTemperature = colorTemperature;
+ this.lastBrightness = lastBrightness;
}
/** @hide */
- public BrightnessChangeEvent(BrightnessChangeEvent other) {
+ public BrightnessChangeEvent(BrightnessChangeEvent other, boolean redactPackage) {
this.brightness = other.brightness;
this.timeStamp = other.timeStamp;
- this.packageName = other.packageName;
+ this.packageName = redactPackage ? null : other.packageName;
this.userId = other.userId;
this.luxValues = other.luxValues;
this.luxTimestamps = other.luxTimestamps;
@@ -118,4 +134,85 @@
dest.writeInt(colorTemperature);
dest.writeFloat(lastBrightness);
}
+
+ /** @hide */
+ public static class Builder {
+ private float mBrightness;
+ private long mTimeStamp;
+ private String mPackageName;
+ private int mUserId;
+ private float[] mLuxValues;
+ private long[] mLuxTimestamps;
+ private float mBatteryLevel;
+ private boolean mNightMode;
+ private int mColorTemperature;
+ private float mLastBrightness;
+
+ /** {@see BrightnessChangeEvent#brightness} */
+ public Builder setBrightness(float brightness) {
+ mBrightness = brightness;
+ return this;
+ }
+
+ /** {@see BrightnessChangeEvent#timeStamp} */
+ public Builder setTimeStamp(long timeStamp) {
+ mTimeStamp = timeStamp;
+ return this;
+ }
+
+ /** {@see BrightnessChangeEvent#packageName} */
+ public Builder setPackageName(String packageName) {
+ mPackageName = packageName;
+ return this;
+ }
+
+ /** {@see BrightnessChangeEvent#userId} */
+ public Builder setUserId(int userId) {
+ mUserId = userId;
+ return this;
+ }
+
+ /** {@see BrightnessChangeEvent#luxValues} */
+ public Builder setLuxValues(float[] luxValues) {
+ mLuxValues = luxValues;
+ return this;
+ }
+
+ /** {@see BrightnessChangeEvent#luxTimestamps} */
+ public Builder setLuxTimestamps(long[] luxTimestamps) {
+ mLuxTimestamps = luxTimestamps;
+ return this;
+ }
+
+ /** {@see BrightnessChangeEvent#batteryLevel} */
+ public Builder setBatteryLevel(float batteryLevel) {
+ mBatteryLevel = batteryLevel;
+ return this;
+ }
+
+ /** {@see BrightnessChangeEvent#nightMode} */
+ public Builder setNightMode(boolean nightMode) {
+ mNightMode = nightMode;
+ return this;
+ }
+
+ /** {@see BrightnessChangeEvent#colorTemperature} */
+ public Builder setColorTemperature(int colorTemperature) {
+ mColorTemperature = colorTemperature;
+ return this;
+ }
+
+ /** {@see BrightnessChangeEvent#lastBrightness} */
+ public Builder setLastBrightness(float lastBrightness) {
+ mLastBrightness = lastBrightness;
+ return this;
+ }
+
+ /** Builds a BrightnessChangeEvent */
+ public BrightnessChangeEvent build() {
+ return new BrightnessChangeEvent(mBrightness, mTimeStamp,
+ mPackageName, mUserId, mLuxValues, mLuxTimestamps, mBatteryLevel,
+ mNightMode, mColorTemperature, mLastBrightness);
+ }
+ }
}
diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java
index 6c3be81..2156491 100644
--- a/core/java/android/hardware/display/BrightnessConfiguration.java
+++ b/core/java/android/hardware/display/BrightnessConfiguration.java
@@ -16,6 +16,8 @@
package android.hardware.display;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Pair;
@@ -25,6 +27,8 @@
import java.util.Arrays;
/** @hide */
+@SystemApi
+@TestApi
public final class BrightnessConfiguration implements Parcelable {
private final float[] mLux;
private final float[] mNits;
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 97e9b9c..76ab35d 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -22,6 +22,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.app.KeyguardManager;
import android.content.Context;
import android.graphics.Point;
@@ -622,6 +623,8 @@
* Fetch {@link BrightnessChangeEvent}s.
* @hide until we make it a system api.
*/
+ @SystemApi
+ @TestApi
@RequiresPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE)
public List<BrightnessChangeEvent> getBrightnessEvents() {
return mGlobal.getBrightnessEvents(mContext.getOpPackageName());
@@ -632,6 +635,9 @@
*
* @hide
*/
+ @SystemApi
+ @TestApi
+ @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
public void setBrightnessConfiguration(BrightnessConfiguration c) {
setBrightnessConfigurationForUser(c, UserHandle.myUserId());
}
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 3f6dd2e..078958a 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -179,6 +179,11 @@
public abstract void persistBrightnessSliderEvents();
/**
+ * Notifies the display manager that resource overlays have changed.
+ */
+ public abstract void onOverlayChanged();
+
+ /**
* Describes the requested power state of the display.
*
* This object is intended to describe the general characteristics of the
diff --git a/core/java/android/hardware/location/ContextHubMessage.java b/core/java/android/hardware/location/ContextHubMessage.java
index 2a4ad00..f85ce3e 100644
--- a/core/java/android/hardware/location/ContextHubMessage.java
+++ b/core/java/android/hardware/location/ContextHubMessage.java
@@ -34,12 +34,11 @@
@SystemApi
@Deprecated
public class ContextHubMessage {
+ private static final int DEBUG_LOG_NUM_BYTES = 16;
private int mType;
private int mVersion;
private byte[]mData;
- private static final String TAG = "ContextHubMessage";
-
/**
* Get the message type
*
@@ -136,4 +135,28 @@
return new ContextHubMessage[size];
}
};
+
+ @Override
+ public String toString() {
+ int length = mData.length;
+
+ String ret =
+ "ContextHubMessage[type = " + mType + ", length = " + mData.length + " bytes](";
+ if (length > 0) {
+ ret += "data = 0x";
+ }
+ for (int i = 0; i < Math.min(length, DEBUG_LOG_NUM_BYTES); i++) {
+ ret += Byte.toHexString(mData[i], true /* upperCase */);
+
+ if ((i + 1) % 4 == 0) {
+ ret += " ";
+ }
+ }
+ if (length > DEBUG_LOG_NUM_BYTES) {
+ ret += "...";
+ }
+ ret += ")";
+
+ return ret;
+ }
}
diff --git a/core/java/android/hardware/location/NanoAppMessage.java b/core/java/android/hardware/location/NanoAppMessage.java
index 716a194..6635258 100644
--- a/core/java/android/hardware/location/NanoAppMessage.java
+++ b/core/java/android/hardware/location/NanoAppMessage.java
@@ -28,6 +28,7 @@
*/
@SystemApi
public final class NanoAppMessage implements Parcelable {
+ private static final int DEBUG_LOG_NUM_BYTES = 16;
private long mNanoAppId;
private int mMessageType;
private byte[] mMessageBody;
@@ -142,4 +143,29 @@
return new NanoAppMessage[size];
}
};
+
+ @Override
+ public String toString() {
+ int length = mMessageBody.length;
+
+ String ret = "NanoAppMessage[type = " + mMessageType + ", length = " + mMessageBody.length
+ + " bytes, " + (mIsBroadcasted ? "broadcast" : "unicast") + ", nanoapp = 0x"
+ + Long.toHexString(mNanoAppId) + "](";
+ if (length > 0) {
+ ret += "data = 0x";
+ }
+ for (int i = 0; i < Math.min(length, DEBUG_LOG_NUM_BYTES); i++) {
+ ret += Byte.toHexString(mMessageBody[i], true /* upperCase */);
+
+ if ((i + 1) % 4 == 0) {
+ ret += " ";
+ }
+ }
+ if (length > DEBUG_LOG_NUM_BYTES) {
+ ret += "...";
+ }
+ ret += ")";
+
+ return ret;
+ }
}
diff --git a/core/java/android/hardware/radio/ITuner.aidl b/core/java/android/hardware/radio/ITuner.aidl
index ca38076..bf5e391 100644
--- a/core/java/android/hardware/radio/ITuner.aidl
+++ b/core/java/android/hardware/radio/ITuner.aidl
@@ -17,6 +17,7 @@
package android.hardware.radio;
import android.graphics.Bitmap;
+import android.hardware.radio.ProgramList;
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
@@ -73,14 +74,8 @@
*/
boolean startBackgroundScan();
- /**
- * @param vendorFilter Vendor-specific filter, must be Map<String, String>
- * @return the list, or null if scan is in progress
- * @throws IllegalArgumentException if invalid arguments are passed
- * @throws IllegalStateException if the scan has not been started, client may
- * call startBackgroundScan to fix this.
- */
- List<RadioManager.ProgramInfo> getProgramList(in Map vendorFilter);
+ void startProgramListUpdates(in ProgramList.Filter filter);
+ void stopProgramListUpdates();
boolean isConfigFlagSupported(int flag);
boolean isConfigFlagSet(int flag);
diff --git a/core/java/android/hardware/radio/ITunerCallback.aidl b/core/java/android/hardware/radio/ITunerCallback.aidl
index 775e25c..54af30f 100644
--- a/core/java/android/hardware/radio/ITunerCallback.aidl
+++ b/core/java/android/hardware/radio/ITunerCallback.aidl
@@ -16,6 +16,7 @@
package android.hardware.radio;
+import android.hardware.radio.ProgramList;
import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioMetadata;
@@ -30,6 +31,7 @@
void onBackgroundScanAvailabilityChange(boolean isAvailable);
void onBackgroundScanComplete();
void onProgramListChanged();
+ void onProgramListUpdated(in ProgramList.Chunk chunk);
/**
* @param parameters Vendor-specific key-value pairs, must be Map<String, String>
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/core/java/android/hardware/radio/ProgramList.aidl
similarity index 77%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to core/java/android/hardware/radio/ProgramList.aidl
index d750363..34b7f97 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/core/java/android/hardware/radio/ProgramList.aidl
@@ -1,5 +1,5 @@
-/*
- * Copyright 2017 The Android Open Source Project
+/**
+ * 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.
@@ -14,7 +14,10 @@
* limitations under the License.
*/
-/** @hide */
-package android.telephony.data;
+package android.hardware.radio;
-parcelable InterfaceAddress;
+/** @hide */
+parcelable ProgramList.Filter;
+
+/** @hide */
+parcelable ProgramList.Chunk;
diff --git a/core/java/android/hardware/radio/ProgramList.java b/core/java/android/hardware/radio/ProgramList.java
new file mode 100644
index 0000000..b2aa9ba
--- /dev/null
+++ b/core/java/android/hardware/radio/ProgramList.java
@@ -0,0 +1,427 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
+
+/**
+ * @hide
+ */
+@SystemApi
+public final class ProgramList implements AutoCloseable {
+
+ private final Object mLock = new Object();
+ private final Map<ProgramSelector.Identifier, RadioManager.ProgramInfo> mPrograms =
+ new HashMap<>();
+
+ private final List<ListCallback> mListCallbacks = new ArrayList<>();
+ private final List<OnCompleteListener> mOnCompleteListeners = new ArrayList<>();
+ private OnCloseListener mOnCloseListener;
+ private boolean mIsClosed = false;
+ private boolean mIsComplete = false;
+
+ ProgramList() {}
+
+ /**
+ * Callback for list change operations.
+ */
+ public abstract static class ListCallback {
+ /**
+ * Called when item was modified or added to the list.
+ */
+ public void onItemChanged(@NonNull ProgramSelector.Identifier id) { }
+
+ /**
+ * Called when item was removed from the list.
+ */
+ public void onItemRemoved(@NonNull ProgramSelector.Identifier id) { }
+ }
+
+ /**
+ * Listener of list complete event.
+ */
+ public interface OnCompleteListener {
+ /**
+ * Called when the list turned complete (i.e. when the scan process
+ * came to an end).
+ */
+ void onComplete();
+ }
+
+ interface OnCloseListener {
+ void onClose();
+ }
+
+ /**
+ * Registers list change callback with executor.
+ */
+ public void registerListCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull ListCallback callback) {
+ registerListCallback(new ListCallback() {
+ public void onItemChanged(@NonNull ProgramSelector.Identifier id) {
+ executor.execute(() -> callback.onItemChanged(id));
+ }
+
+ public void onItemRemoved(@NonNull ProgramSelector.Identifier id) {
+ executor.execute(() -> callback.onItemRemoved(id));
+ }
+ });
+ }
+
+ /**
+ * Registers list change callback.
+ */
+ public void registerListCallback(@NonNull ListCallback callback) {
+ synchronized (mLock) {
+ if (mIsClosed) return;
+ mListCallbacks.add(Objects.requireNonNull(callback));
+ }
+ }
+
+ /**
+ * Unregisters list change callback.
+ */
+ public void unregisterListCallback(@NonNull ListCallback callback) {
+ synchronized (mLock) {
+ if (mIsClosed) return;
+ mListCallbacks.remove(Objects.requireNonNull(callback));
+ }
+ }
+
+ /**
+ * Adds list complete event listener with executor.
+ */
+ public void addOnCompleteListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull OnCompleteListener listener) {
+ addOnCompleteListener(() -> executor.execute(listener::onComplete));
+ }
+
+ /**
+ * Adds list complete event listener.
+ */
+ public void addOnCompleteListener(@NonNull OnCompleteListener listener) {
+ synchronized (mLock) {
+ if (mIsClosed) return;
+ mOnCompleteListeners.add(Objects.requireNonNull(listener));
+ if (mIsComplete) listener.onComplete();
+ }
+ }
+
+ /**
+ * Removes list complete event listener.
+ */
+ public void removeOnCompleteListener(@NonNull OnCompleteListener listener) {
+ synchronized (mLock) {
+ if (mIsClosed) return;
+ mOnCompleteListeners.remove(Objects.requireNonNull(listener));
+ }
+ }
+
+ void setOnCloseListener(@Nullable OnCloseListener listener) {
+ synchronized (mLock) {
+ if (mOnCloseListener != null) {
+ throw new IllegalStateException("Close callback is already set");
+ }
+ mOnCloseListener = listener;
+ }
+ }
+
+ /**
+ * Disables list updates and releases all resources.
+ */
+ public void close() {
+ synchronized (mLock) {
+ if (mIsClosed) return;
+ mIsClosed = true;
+ mPrograms.clear();
+ mListCallbacks.clear();
+ mOnCompleteListeners.clear();
+ if (mOnCloseListener != null) {
+ mOnCloseListener.onClose();
+ mOnCloseListener = null;
+ }
+ }
+ }
+
+ void apply(@NonNull Chunk chunk) {
+ synchronized (mLock) {
+ if (mIsClosed) return;
+
+ mIsComplete = false;
+
+ if (chunk.isPurge()) {
+ new HashSet<>(mPrograms.keySet()).stream().forEach(id -> removeLocked(id));
+ }
+
+ chunk.getRemoved().stream().forEach(id -> removeLocked(id));
+ chunk.getModified().stream().forEach(info -> putLocked(info));
+
+ if (chunk.isComplete()) {
+ mIsComplete = true;
+ mOnCompleteListeners.forEach(cb -> cb.onComplete());
+ }
+ }
+ }
+
+ private void putLocked(@NonNull RadioManager.ProgramInfo value) {
+ ProgramSelector.Identifier key = value.getSelector().getPrimaryId();
+ mPrograms.put(Objects.requireNonNull(key), value);
+ ProgramSelector.Identifier sel = value.getSelector().getPrimaryId();
+ mListCallbacks.forEach(cb -> cb.onItemChanged(sel));
+ }
+
+ private void removeLocked(@NonNull ProgramSelector.Identifier key) {
+ RadioManager.ProgramInfo removed = mPrograms.remove(Objects.requireNonNull(key));
+ if (removed == null) return;
+ ProgramSelector.Identifier sel = removed.getSelector().getPrimaryId();
+ mListCallbacks.forEach(cb -> cb.onItemRemoved(sel));
+ }
+
+ /**
+ * Converts the program list in its current shape to the static List<>.
+ *
+ * @return the new List<> object; it won't receive any further updates
+ */
+ public @NonNull List<RadioManager.ProgramInfo> toList() {
+ synchronized (mLock) {
+ return mPrograms.values().stream().collect(Collectors.toList());
+ }
+ }
+
+ /**
+ * Returns the program with a specified primary identifier.
+ *
+ * @param id primary identifier of a program to fetch
+ * @return the program info, or null if there is no such program on the list
+ */
+ public @Nullable RadioManager.ProgramInfo get(@NonNull ProgramSelector.Identifier id) {
+ synchronized (mLock) {
+ return mPrograms.get(Objects.requireNonNull(id));
+ }
+ }
+
+ /**
+ * Filter for the program list.
+ */
+ public static final class Filter implements Parcelable {
+ private final @NonNull Set<Integer> mIdentifierTypes;
+ private final @NonNull Set<ProgramSelector.Identifier> mIdentifiers;
+ private final boolean mIncludeCategories;
+ private final boolean mExcludeModifications;
+ private final @Nullable Map<String, String> mVendorFilter;
+
+ /**
+ * Constructor of program list filter.
+ *
+ * Arrays passed to this constructor become owned by this object, do not modify them later.
+ *
+ * @param identifierTypes see getIdentifierTypes()
+ * @param identifiers see getIdentifiers()
+ * @param includeCategories see areCategoriesIncluded()
+ * @param excludeModifications see areModificationsExcluded()
+ */
+ public Filter(@NonNull Set<Integer> identifierTypes,
+ @NonNull Set<ProgramSelector.Identifier> identifiers,
+ boolean includeCategories, boolean excludeModifications) {
+ mIdentifierTypes = Objects.requireNonNull(identifierTypes);
+ mIdentifiers = Objects.requireNonNull(identifiers);
+ mIncludeCategories = includeCategories;
+ mExcludeModifications = excludeModifications;
+ mVendorFilter = null;
+ }
+
+ /**
+ * @hide for framework use only
+ */
+ public Filter(@Nullable Map<String, String> vendorFilter) {
+ mIdentifierTypes = Collections.emptySet();
+ mIdentifiers = Collections.emptySet();
+ mIncludeCategories = false;
+ mExcludeModifications = false;
+ mVendorFilter = vendorFilter;
+ }
+
+ private Filter(@NonNull Parcel in) {
+ mIdentifierTypes = Utils.createIntSet(in);
+ mIdentifiers = Utils.createSet(in, ProgramSelector.Identifier.CREATOR);
+ mIncludeCategories = in.readByte() != 0;
+ mExcludeModifications = in.readByte() != 0;
+ mVendorFilter = Utils.readStringMap(in);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ Utils.writeIntSet(dest, mIdentifierTypes);
+ Utils.writeSet(dest, mIdentifiers);
+ dest.writeByte((byte) (mIncludeCategories ? 1 : 0));
+ dest.writeByte((byte) (mExcludeModifications ? 1 : 0));
+ Utils.writeStringMap(dest, mVendorFilter);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<Filter> CREATOR = new Parcelable.Creator<Filter>() {
+ public Filter createFromParcel(Parcel in) {
+ return new Filter(in);
+ }
+
+ public Filter[] newArray(int size) {
+ return new Filter[size];
+ }
+ };
+
+ /**
+ * @hide for framework use only
+ */
+ public Map<String, String> getVendorFilter() {
+ return mVendorFilter;
+ }
+
+ /**
+ * Returns the list of identifier types that satisfy the filter.
+ *
+ * If the program list entry contains at least one identifier of the type
+ * listed, it satisfies this condition.
+ *
+ * Empty list means no filtering on identifier type.
+ *
+ * @return the list of accepted identifier types, must not be modified
+ */
+ public @NonNull Set<Integer> getIdentifierTypes() {
+ return mIdentifierTypes;
+ }
+
+ /**
+ * Returns the list of identifiers that satisfy the filter.
+ *
+ * If the program list entry contains at least one listed identifier,
+ * it satisfies this condition.
+ *
+ * Empty list means no filtering on identifier.
+ *
+ * @return the list of accepted identifiers, must not be modified
+ */
+ public @NonNull Set<ProgramSelector.Identifier> getIdentifiers() {
+ return mIdentifiers;
+ }
+
+ /**
+ * Checks, if non-tunable entries that define tree structure on the
+ * program list (i.e. DAB ensembles) should be included.
+ */
+ public boolean areCategoriesIncluded() {
+ return mIncludeCategories;
+ }
+
+ /**
+ * Checks, if updates on entry modifications should be disabled.
+ *
+ * If true, 'modified' vector of ProgramListChunk must contain list
+ * additions only. Once the program is added to the list, it's not
+ * updated anymore.
+ */
+ public boolean areModificationsExcluded() {
+ return mExcludeModifications;
+ }
+ }
+
+ /**
+ * @hide This is a transport class used for internal communication between
+ * Broadcast Radio Service and RadioManager.
+ * Do not use it directly.
+ */
+ public static final class Chunk implements Parcelable {
+ private final boolean mPurge;
+ private final boolean mComplete;
+ private final @NonNull Set<RadioManager.ProgramInfo> mModified;
+ private final @NonNull Set<ProgramSelector.Identifier> mRemoved;
+
+ public Chunk(boolean purge, boolean complete,
+ @Nullable Set<RadioManager.ProgramInfo> modified,
+ @Nullable Set<ProgramSelector.Identifier> removed) {
+ mPurge = purge;
+ mComplete = complete;
+ mModified = (modified != null) ? modified : Collections.emptySet();
+ mRemoved = (removed != null) ? removed : Collections.emptySet();
+ }
+
+ private Chunk(@NonNull Parcel in) {
+ mPurge = in.readByte() != 0;
+ mComplete = in.readByte() != 0;
+ mModified = Utils.createSet(in, RadioManager.ProgramInfo.CREATOR);
+ mRemoved = Utils.createSet(in, ProgramSelector.Identifier.CREATOR);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByte((byte) (mPurge ? 1 : 0));
+ dest.writeByte((byte) (mComplete ? 1 : 0));
+ Utils.writeSet(dest, mModified);
+ Utils.writeSet(dest, mRemoved);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<Chunk> CREATOR = new Parcelable.Creator<Chunk>() {
+ public Chunk createFromParcel(Parcel in) {
+ return new Chunk(in);
+ }
+
+ public Chunk[] newArray(int size) {
+ return new Chunk[size];
+ }
+ };
+
+ public boolean isPurge() {
+ return mPurge;
+ }
+
+ public boolean isComplete() {
+ return mComplete;
+ }
+
+ public @NonNull Set<RadioManager.ProgramInfo> getModified() {
+ return mModified;
+ }
+
+ public @NonNull Set<ProgramSelector.Identifier> getRemoved() {
+ return mRemoved;
+ }
+ }
+}
diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java
index 2211cee..3556751 100644
--- a/core/java/android/hardware/radio/ProgramSelector.java
+++ b/core/java/android/hardware/radio/ProgramSelector.java
@@ -59,6 +59,7 @@
*/
@SystemApi
public final class ProgramSelector implements Parcelable {
+ public static final int PROGRAM_TYPE_INVALID = 0;
/** Analogue AM radio (with or without RDS). */
public static final int PROGRAM_TYPE_AM = 1;
/** analogue FM radio (with or without RDS). */
@@ -77,6 +78,7 @@
public static final int PROGRAM_TYPE_VENDOR_START = 1000;
public static final int PROGRAM_TYPE_VENDOR_END = 1999;
@IntDef(prefix = { "PROGRAM_TYPE_" }, value = {
+ PROGRAM_TYPE_INVALID,
PROGRAM_TYPE_AM,
PROGRAM_TYPE_FM,
PROGRAM_TYPE_AM_HD,
@@ -89,6 +91,7 @@
@Retention(RetentionPolicy.SOURCE)
public @interface ProgramType {}
+ public static final int IDENTIFIER_TYPE_INVALID = 0;
/** kHz */
public static final int IDENTIFIER_TYPE_AMFM_FREQUENCY = 1;
/** 16bit */
@@ -148,6 +151,7 @@
public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = PROGRAM_TYPE_VENDOR_START;
public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = PROGRAM_TYPE_VENDOR_END;
@IntDef(prefix = { "IDENTIFIER_TYPE_" }, value = {
+ IDENTIFIER_TYPE_INVALID,
IDENTIFIER_TYPE_AMFM_FREQUENCY,
IDENTIFIER_TYPE_RDS_PI,
IDENTIFIER_TYPE_HD_STATION_ID_EXT,
@@ -268,7 +272,7 @@
* Vendor identifiers are passed as-is to the HAL implementation,
* preserving elements order.
*
- * @return a array of vendor identifiers, must not be modified.
+ * @return an array of vendor identifiers, must not be modified.
*/
public @NonNull long[] getVendorIds() {
return mVendorIds;
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index b740f14..56668ac 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -185,25 +185,6 @@
@Retention(RetentionPolicy.SOURCE)
public @interface ConfigFlag {}
- private static void writeStringMap(@NonNull Parcel dest, @NonNull Map<String, String> map) {
- dest.writeInt(map.size());
- for (Map.Entry<String, String> entry : map.entrySet()) {
- dest.writeString(entry.getKey());
- dest.writeString(entry.getValue());
- }
- }
-
- private static @NonNull Map<String, String> readStringMap(@NonNull Parcel in) {
- int size = in.readInt();
- Map<String, String> map = new HashMap<>();
- while (size-- > 0) {
- String key = in.readString();
- String value = in.readString();
- map.put(key, value);
- }
- return map;
- }
-
/*****************************************************************************
* Lists properties, options and radio bands supported by a given broadcast radio module.
* Each module has a unique ID used to address it when calling RadioManager APIs.
@@ -415,7 +396,7 @@
mIsBgScanSupported = in.readInt() == 1;
mSupportedProgramTypes = arrayToSet(in.createIntArray());
mSupportedIdentifierTypes = arrayToSet(in.createIntArray());
- mVendorInfo = readStringMap(in);
+ mVendorInfo = Utils.readStringMap(in);
}
public static final Parcelable.Creator<ModuleProperties> CREATOR
@@ -445,7 +426,7 @@
dest.writeInt(mIsBgScanSupported ? 1 : 0);
dest.writeIntArray(setToArray(mSupportedProgramTypes));
dest.writeIntArray(setToArray(mSupportedIdentifierTypes));
- writeStringMap(dest, mVendorInfo);
+ Utils.writeStringMap(dest, mVendorInfo);
}
@Override
@@ -1410,7 +1391,7 @@
private static final int FLAG_TRAFFIC_ANNOUNCEMENT = 1 << 3;
@NonNull private final ProgramSelector mSelector;
- private final boolean mTuned;
+ private final boolean mTuned; // TODO(b/69958777): replace with mFlags
private final boolean mStereo;
private final boolean mDigital;
private final int mFlags;
@@ -1418,7 +1399,8 @@
private final RadioMetadata mMetadata;
@NonNull private final Map<String, String> mVendorInfo;
- ProgramInfo(@NonNull ProgramSelector selector, boolean tuned, boolean stereo,
+ /** @hide */
+ public ProgramInfo(@NonNull ProgramSelector selector, boolean tuned, boolean stereo,
boolean digital, int signalStrength, RadioMetadata metadata, int flags,
Map<String, String> vendorInfo) {
mSelector = selector;
@@ -1564,7 +1546,7 @@
mMetadata = null;
}
mFlags = in.readInt();
- mVendorInfo = readStringMap(in);
+ mVendorInfo = Utils.readStringMap(in);
}
public static final Parcelable.Creator<ProgramInfo> CREATOR
@@ -1592,7 +1574,7 @@
mMetadata.writeToParcel(dest, flags);
}
dest.writeInt(mFlags);
- writeStringMap(dest, mVendorInfo);
+ Utils.writeStringMap(dest, mVendorInfo);
}
@Override
@@ -1727,7 +1709,8 @@
Log.e(TAG, "Failed to open tuner");
return null;
}
- return new TunerAdapter(tuner, config != null ? config.getType() : BAND_INVALID);
+ return new TunerAdapter(tuner, halCallback,
+ config != null ? config.getType() : BAND_INVALID);
}
@NonNull private final Context mContext;
diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java
index 0d367e7..ed20c4a 100644
--- a/core/java/android/hardware/radio/RadioTuner.java
+++ b/core/java/android/hardware/radio/RadioTuner.java
@@ -280,11 +280,29 @@
* @throws IllegalStateException if the scan is in progress or has not been started,
* startBackgroundScan() call may fix it.
* @throws IllegalArgumentException if the vendorFilter argument is not valid.
+ * @deprecated Use {@link getDynamicProgramList} instead.
*/
+ @Deprecated
public abstract @NonNull List<RadioManager.ProgramInfo>
getProgramList(@Nullable Map<String, String> vendorFilter);
/**
+ * Get the dynamic list of discovered radio stations.
+ *
+ * The list object is updated asynchronously; to get the updates register
+ * with {@link ProgramList#addListCallback}.
+ *
+ * When the returned object is no longer used, it must be closed.
+ *
+ * @param filter filter for the list, or null to get the full list.
+ * @return the dynamic program list object, close it after use
+ * or {@code null} if program list is not supported by the tuner
+ */
+ public @Nullable ProgramList getDynamicProgramList(@Nullable ProgramList.Filter filter) {
+ return null;
+ }
+
+ /**
* Checks, if the analog playback is forced, see setAnalogForced.
*
* @throws IllegalStateException if the switch is not supported at current
diff --git a/core/java/android/hardware/radio/TunerAdapter.java b/core/java/android/hardware/radio/TunerAdapter.java
index 8ad609d..91944bf 100644
--- a/core/java/android/hardware/radio/TunerAdapter.java
+++ b/core/java/android/hardware/radio/TunerAdapter.java
@@ -33,15 +33,18 @@
private static final String TAG = "BroadcastRadio.TunerAdapter";
@NonNull private final ITuner mTuner;
+ @NonNull private final TunerCallbackAdapter mCallback;
private boolean mIsClosed = false;
private @RadioManager.Band int mBand;
- TunerAdapter(ITuner tuner, @RadioManager.Band int band) {
- if (tuner == null) {
- throw new NullPointerException();
- }
- mTuner = tuner;
+ private ProgramList mLegacyListProxy;
+ private Map<String, String> mLegacyListFilter;
+
+ TunerAdapter(@NonNull ITuner tuner, @NonNull TunerCallbackAdapter callback,
+ @RadioManager.Band int band) {
+ mTuner = Objects.requireNonNull(tuner);
+ mCallback = Objects.requireNonNull(callback);
mBand = band;
}
@@ -53,6 +56,10 @@
return;
}
mIsClosed = true;
+ if (mLegacyListProxy != null) {
+ mLegacyListProxy.close();
+ mLegacyListProxy = null;
+ }
}
try {
mTuner.close();
@@ -227,10 +234,55 @@
@Override
public @NonNull List<RadioManager.ProgramInfo>
getProgramList(@Nullable Map<String, String> vendorFilter) {
- try {
- return mTuner.getProgramList(vendorFilter);
- } catch (RemoteException e) {
- throw new RuntimeException("service died", e);
+ synchronized (mTuner) {
+ if (mLegacyListProxy == null || !Objects.equals(mLegacyListFilter, vendorFilter)) {
+ Log.i(TAG, "Program list filter has changed, requesting new list");
+ mLegacyListProxy = new ProgramList();
+ mLegacyListFilter = vendorFilter;
+
+ mCallback.clearLastCompleteList();
+ mCallback.setProgramListObserver(mLegacyListProxy, () -> { });
+ try {
+ mTuner.startProgramListUpdates(new ProgramList.Filter(vendorFilter));
+ } catch (RemoteException ex) {
+ throw new RuntimeException("service died", ex);
+ }
+ }
+
+ List<RadioManager.ProgramInfo> list = mCallback.getLastCompleteList();
+ if (list == null) throw new IllegalStateException("Program list is not ready yet");
+ return list;
+ }
+ }
+
+ @Override
+ public @Nullable ProgramList getDynamicProgramList(@Nullable ProgramList.Filter filter) {
+ synchronized (mTuner) {
+ if (mLegacyListProxy != null) {
+ mLegacyListProxy.close();
+ mLegacyListProxy = null;
+ }
+ mLegacyListFilter = null;
+
+ ProgramList list = new ProgramList();
+ mCallback.setProgramListObserver(list, () -> {
+ try {
+ mTuner.stopProgramListUpdates();
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Couldn't stop program list updates", ex);
+ }
+ });
+
+ try {
+ mTuner.startProgramListUpdates(filter);
+ } catch (UnsupportedOperationException ex) {
+ return null;
+ } catch (RemoteException ex) {
+ mCallback.setProgramListObserver(null, () -> { });
+ throw new RuntimeException("service died", ex);
+ }
+
+ return list;
}
}
diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java
index a01f658..b299ffe 100644
--- a/core/java/android/hardware/radio/TunerCallbackAdapter.java
+++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java
@@ -22,7 +22,9 @@
import android.os.Looper;
import android.util.Log;
+import java.util.List;
import java.util.Map;
+import java.util.Objects;
/**
* Implements the ITunerCallback interface by forwarding calls to RadioTuner.Callback.
@@ -30,9 +32,14 @@
class TunerCallbackAdapter extends ITunerCallback.Stub {
private static final String TAG = "BroadcastRadio.TunerCallbackAdapter";
+ private final Object mLock = new Object();
@NonNull private final RadioTuner.Callback mCallback;
@NonNull private final Handler mHandler;
+ @Nullable ProgramList mProgramList;
+ @Nullable List<RadioManager.ProgramInfo> mLastCompleteList; // for legacy getProgramList call
+ private boolean mDelayedCompleteCallback = false;
+
TunerCallbackAdapter(@NonNull RadioTuner.Callback callback, @Nullable Handler handler) {
mCallback = callback;
if (handler == null) {
@@ -42,6 +49,49 @@
}
}
+ void setProgramListObserver(@Nullable ProgramList programList,
+ @NonNull ProgramList.OnCloseListener closeListener) {
+ Objects.requireNonNull(closeListener);
+ synchronized (mLock) {
+ if (mProgramList != null) {
+ Log.w(TAG, "Previous program list observer wasn't properly closed, closing it...");
+ mProgramList.close();
+ }
+ mProgramList = programList;
+ if (programList == null) return;
+ programList.setOnCloseListener(() -> {
+ synchronized (mLock) {
+ if (mProgramList != programList) return;
+ mProgramList = null;
+ mLastCompleteList = null;
+ closeListener.onClose();
+ }
+ });
+ programList.addOnCompleteListener(() -> {
+ synchronized (mLock) {
+ if (mProgramList != programList) return;
+ mLastCompleteList = programList.toList();
+ if (mDelayedCompleteCallback) {
+ Log.d(TAG, "Sending delayed onBackgroundScanComplete callback");
+ sendBackgroundScanCompleteLocked();
+ }
+ }
+ });
+ }
+ }
+
+ @Nullable List<RadioManager.ProgramInfo> getLastCompleteList() {
+ synchronized (mLock) {
+ return mLastCompleteList;
+ }
+ }
+
+ void clearLastCompleteList() {
+ synchronized (mLock) {
+ mLastCompleteList = null;
+ }
+ }
+
@Override
public void onError(int status) {
mHandler.post(() -> mCallback.onError(status));
@@ -87,9 +137,22 @@
mHandler.post(() -> mCallback.onBackgroundScanAvailabilityChange(isAvailable));
}
+ private void sendBackgroundScanCompleteLocked() {
+ mDelayedCompleteCallback = false;
+ mHandler.post(() -> mCallback.onBackgroundScanComplete());
+ }
+
@Override
public void onBackgroundScanComplete() {
- mHandler.post(() -> mCallback.onBackgroundScanComplete());
+ synchronized (mLock) {
+ if (mLastCompleteList == null) {
+ Log.i(TAG, "Got onBackgroundScanComplete callback, but the "
+ + "program list didn't get through yet. Delaying it...");
+ mDelayedCompleteCallback = true;
+ return;
+ }
+ sendBackgroundScanCompleteLocked();
+ }
}
@Override
@@ -98,6 +161,14 @@
}
@Override
+ public void onProgramListUpdated(ProgramList.Chunk chunk) {
+ synchronized (mLock) {
+ if (mProgramList == null) return;
+ mProgramList.apply(Objects.requireNonNull(chunk));
+ }
+ }
+
+ @Override
public void onParametersUpdated(Map parameters) {
mHandler.post(() -> mCallback.onParametersUpdated(parameters));
}
diff --git a/core/java/android/hardware/radio/Utils.java b/core/java/android/hardware/radio/Utils.java
new file mode 100644
index 0000000..09bf8fe
--- /dev/null
+++ b/core/java/android/hardware/radio/Utils.java
@@ -0,0 +1,92 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+final class Utils {
+ static void writeStringMap(@NonNull Parcel dest, @Nullable Map<String, String> map) {
+ if (map == null) {
+ dest.writeInt(0);
+ return;
+ }
+ dest.writeInt(map.size());
+ for (Map.Entry<String, String> entry : map.entrySet()) {
+ dest.writeString(entry.getKey());
+ dest.writeString(entry.getValue());
+ }
+ }
+
+ static @NonNull Map<String, String> readStringMap(@NonNull Parcel in) {
+ int size = in.readInt();
+ Map<String, String> map = new HashMap<>();
+ while (size-- > 0) {
+ String key = in.readString();
+ String value = in.readString();
+ map.put(key, value);
+ }
+ return map;
+ }
+
+ static <T extends Parcelable> void writeSet(@NonNull Parcel dest, @Nullable Set<T> set) {
+ if (set == null) {
+ dest.writeInt(0);
+ return;
+ }
+ dest.writeInt(set.size());
+ set.stream().forEach(elem -> dest.writeTypedObject(elem, 0));
+ }
+
+ static <T> Set<T> createSet(@NonNull Parcel in, Parcelable.Creator<T> c) {
+ int size = in.readInt();
+ Set<T> set = new HashSet<>();
+ while (size-- > 0) {
+ set.add(in.readTypedObject(c));
+ }
+ return set;
+ }
+
+ static void writeIntSet(@NonNull Parcel dest, @Nullable Set<Integer> set) {
+ if (set == null) {
+ dest.writeInt(0);
+ return;
+ }
+ dest.writeInt(set.size());
+ set.stream().forEach(elem -> dest.writeInt(Objects.requireNonNull(elem)));
+ }
+
+ static Set<Integer> createIntSet(@NonNull Parcel in) {
+ return createSet(in, new Parcelable.Creator<Integer>() {
+ public Integer createFromParcel(Parcel in) {
+ return in.readInt();
+ }
+
+ public Integer[] newArray(int size) {
+ return new Integer[size];
+ }
+ });
+ }
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 02b1c65..7528bc3 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -18,6 +18,7 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import android.annotation.CallSuper;
import android.annotation.DrawableRes;
@@ -339,42 +340,35 @@
final Insets mTmpInsets = new Insets();
final int[] mTmpLocation = new int[2];
- final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
- new ViewTreeObserver.OnComputeInternalInsetsListener() {
- public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
- if (isExtractViewShown()) {
- // In true fullscreen mode, we just say the window isn't covering
- // any content so we don't impact whatever is behind.
- View decor = getWindow().getWindow().getDecorView();
- info.contentInsets.top = info.visibleInsets.top
- = decor.getHeight();
- info.touchableRegion.setEmpty();
- info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
- } else {
- onComputeInsets(mTmpInsets);
- info.contentInsets.top = mTmpInsets.contentTopInsets;
- info.visibleInsets.top = mTmpInsets.visibleTopInsets;
- info.touchableRegion.set(mTmpInsets.touchableRegion);
- info.setTouchableInsets(mTmpInsets.touchableInsets);
+ final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> {
+ if (isExtractViewShown()) {
+ // In true fullscreen mode, we just say the window isn't covering
+ // any content so we don't impact whatever is behind.
+ View decor = getWindow().getWindow().getDecorView();
+ info.contentInsets.top = info.visibleInsets.top = decor.getHeight();
+ info.touchableRegion.setEmpty();
+ info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+ } else {
+ onComputeInsets(mTmpInsets);
+ info.contentInsets.top = mTmpInsets.contentTopInsets;
+ info.visibleInsets.top = mTmpInsets.visibleTopInsets;
+ info.touchableRegion.set(mTmpInsets.touchableRegion);
+ info.setTouchableInsets(mTmpInsets.touchableInsets);
+ }
+ };
+
+ final View.OnClickListener mActionClickListener = v -> {
+ final EditorInfo ei = getCurrentInputEditorInfo();
+ final InputConnection ic = getCurrentInputConnection();
+ if (ei != null && ic != null) {
+ if (ei.actionId != 0) {
+ ic.performEditorAction(ei.actionId);
+ } else if ((ei.imeOptions & EditorInfo.IME_MASK_ACTION) != EditorInfo.IME_ACTION_NONE) {
+ ic.performEditorAction(ei.imeOptions & EditorInfo.IME_MASK_ACTION);
}
}
};
- final View.OnClickListener mActionClickListener = new View.OnClickListener() {
- public void onClick(View v) {
- final EditorInfo ei = getCurrentInputEditorInfo();
- final InputConnection ic = getCurrentInputConnection();
- if (ei != null && ic != null) {
- if (ei.actionId != 0) {
- ic.performEditorAction(ei.actionId);
- } else if ((ei.imeOptions&EditorInfo.IME_MASK_ACTION)
- != EditorInfo.IME_ACTION_NONE) {
- ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION);
- }
- }
- }
- };
-
/**
* Concrete implementation of
* {@link AbstractInputMethodService.AbstractInputMethodImpl} that provides
@@ -852,6 +846,11 @@
Context.LAYOUT_INFLATER_SERVICE);
mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
+ // For ColorView in DecorView to work, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS needs to be set
+ // by default (but IME developers can opt this out later if they want a new behavior).
+ mWindow.getWindow().setFlags(
+ FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+
initViews();
mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
}
@@ -882,8 +881,6 @@
mThemeAttrs = obtainStyledAttributes(android.R.styleable.InputMethodService);
mRootView = mInflater.inflate(
com.android.internal.R.layout.input_method, null);
- mRootView.setSystemUiVisibility(
- View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
mWindow.setContentView(mRootView);
mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsComputer);
mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
@@ -892,20 +889,20 @@
mWindow.getWindow().setWindowAnimations(
com.android.internal.R.style.Animation_InputMethodFancy);
}
- mFullscreenArea = (ViewGroup)mRootView.findViewById(com.android.internal.R.id.fullscreenArea);
+ mFullscreenArea = mRootView.findViewById(com.android.internal.R.id.fullscreenArea);
mExtractViewHidden = false;
- mExtractFrame = (FrameLayout)mRootView.findViewById(android.R.id.extractArea);
+ mExtractFrame = mRootView.findViewById(android.R.id.extractArea);
mExtractView = null;
mExtractEditText = null;
mExtractAccessories = null;
mExtractAction = null;
mFullscreenApplied = false;
-
- mCandidatesFrame = (FrameLayout)mRootView.findViewById(android.R.id.candidatesArea);
- mInputFrame = (FrameLayout)mRootView.findViewById(android.R.id.inputArea);
+
+ mCandidatesFrame = mRootView.findViewById(android.R.id.candidatesArea);
+ mInputFrame = mRootView.findViewById(android.R.id.inputArea);
mInputView = null;
mIsInputViewShown = false;
-
+
mExtractFrame.setVisibility(View.GONE);
mCandidatesVisibility = getCandidatesHiddenVisibility();
mCandidatesFrame.setVisibility(mCandidatesVisibility);
@@ -1085,33 +1082,6 @@
}
/**
- * Close/hide the input method's soft input area, so the user no longer
- * sees it or can interact with it. This can only be called
- * from the currently active input method, as validated by the given token.
- *
- * @param flags Provides additional operating flags. Currently may be
- * 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY},
- * {@link InputMethodManager#HIDE_NOT_ALWAYS} bit set.
- */
- public void hideSoftInputFromInputMethod(int flags) {
- mImm.hideSoftInputFromInputMethodInternal(mToken, flags);
- }
-
- /**
- * Show the input method's soft input area, so the user
- * sees the input method window and can interact with it.
- * This can only be called from the currently active input method,
- * as validated by the given token.
- *
- * @param flags Provides additional operating flags. Currently may be
- * 0 or have the {@link InputMethodManager#SHOW_IMPLICIT} or
- * {@link InputMethodManager#SHOW_FORCED} bit set.
- */
- public void showSoftInputFromInputMethod(int flags) {
- mImm.showSoftInputFromInputMethodInternal(mToken, flags);
- }
-
- /**
* Force switch to the last used input method and subtype. If the last input method didn't have
* any subtypes, the framework will simply switch to the last input method with no subtype
* specified.
@@ -1457,17 +1427,17 @@
public int getCandidatesHiddenVisibility() {
return isExtractViewShown() ? View.GONE : View.INVISIBLE;
}
-
+
public void showStatusIcon(@DrawableRes int iconResId) {
mStatusIcon = iconResId;
- mImm.showStatusIcon(mToken, getPackageName(), iconResId);
+ mImm.showStatusIconInternal(mToken, getPackageName(), iconResId);
}
-
+
public void hideStatusIcon() {
mStatusIcon = 0;
- mImm.hideStatusIcon(mToken);
+ mImm.hideStatusIconInternal(mToken);
}
-
+
/**
* Force switch to a new input method, as identified by <var>id</var>. This
* input method will be destroyed, and the requested one started on the
@@ -1476,9 +1446,9 @@
* @param id Unique identifier of the new input method ot start.
*/
public void switchInputMethod(String id) {
- mImm.setInputMethod(mToken, id);
+ mImm.setInputMethodInternal(mToken, id);
}
-
+
public void setExtractView(View view) {
mExtractFrame.removeAllViews();
mExtractFrame.addView(view, new FrameLayout.LayoutParams(
@@ -1486,13 +1456,13 @@
ViewGroup.LayoutParams.MATCH_PARENT));
mExtractView = view;
if (view != null) {
- mExtractEditText = (ExtractEditText)view.findViewById(
+ mExtractEditText = view.findViewById(
com.android.internal.R.id.inputExtractEditText);
mExtractEditText.setIME(this);
mExtractAction = view.findViewById(
com.android.internal.R.id.inputExtractAction);
if (mExtractAction != null) {
- mExtractAccessories = (ViewGroup)view.findViewById(
+ mExtractAccessories = view.findViewById(
com.android.internal.R.id.inputExtractAccessories);
}
startExtractingText(false);
@@ -1741,7 +1711,7 @@
// Rethrow the exception to preserve the existing behavior. Some IMEs may have directly
// called this method and relied on this exception for some clean-up tasks.
// TODO: Give developers a clear guideline of whether it's OK to call this method or
- // InputMethodManager#showSoftInputFromInputMethod() should always be used instead.
+ // InputMethodService#requestShowSelf(int) should always be used instead.
throw e;
} finally {
// TODO: Is it OK to set true when we get BadTokenException?
@@ -2063,27 +2033,30 @@
/**
* Close this input method's soft input area, removing it from the display.
- * The input method will continue running, but the user can no longer use
- * it to generate input by touching the screen.
- * @param flags Provides additional operating flags. Currently may be
- * 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY
- * InputMethodManager.HIDE_IMPLICIT_ONLY} bit set.
+ *
+ * The input method will continue running, but the user can no longer use it to generate input
+ * by touching the screen.
+ *
+ * @see InputMethodManager#HIDE_IMPLICIT_ONLY
+ * @see InputMethodManager#HIDE_NOT_ALWAYS
+ * @param flags Provides additional operating flags.
*/
public void requestHideSelf(int flags) {
- mImm.hideSoftInputFromInputMethod(mToken, flags);
+ mImm.hideSoftInputFromInputMethodInternal(mToken, flags);
}
-
+
/**
- * Show the input method. This is a call back to the
- * IMF to handle showing the input method.
- * @param flags Provides additional operating flags. Currently may be
- * 0 or have the {@link InputMethodManager#SHOW_FORCED
- * InputMethodManager.} bit set.
+ * Show the input method's soft input area, so the user sees the input method window and can
+ * interact with it.
+ *
+ * @see InputMethodManager#SHOW_IMPLICIT
+ * @see InputMethodManager#SHOW_FORCED
+ * @param flags Provides additional operating flags.
*/
- private void requestShowSelf(int flags) {
- mImm.showSoftInputFromInputMethod(mToken, flags);
+ public void requestShowSelf(int flags) {
+ mImm.showSoftInputFromInputMethodInternal(mToken, flags);
}
-
+
private boolean handleBack(boolean doIt) {
if (mShowInputRequested) {
// If the soft input area is shown, back closes it and we
@@ -2750,7 +2723,7 @@
* application.
* This cannot be {@code null}.
* @param inputConnection {@link InputConnection} with which
- * {@link InputConnection#commitContent(InputContentInfo, Bundle)} will be called.
+ * {@link InputConnection#commitContent(InputContentInfo, int, Bundle)} will be called.
* @hide
*/
@Override
diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl
index d9b57db..790c80b 100644
--- a/core/java/android/net/IIpSecService.aidl
+++ b/core/java/android/net/IIpSecService.aidl
@@ -31,7 +31,7 @@
interface IIpSecService
{
IpSecSpiResponse allocateSecurityParameterIndex(
- int direction, in String remoteAddress, int requestedSpi, in IBinder binder);
+ in String destinationAddress, int requestedSpi, in IBinder binder);
void releaseSecurityParameterIndex(int resourceId);
@@ -43,7 +43,7 @@
void deleteTransportModeTransform(int transformId);
- void applyTransportModeTransform(in ParcelFileDescriptor socket, int transformId);
+ void applyTransportModeTransform(in ParcelFileDescriptor socket, int direction, int transformId);
- void removeTransportModeTransform(in ParcelFileDescriptor socket, int transformId);
+ void removeTransportModeTransforms(in ParcelFileDescriptor socket);
}
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 7d752e8..c69a4d4 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -256,13 +256,19 @@
return getName().equals(AUTH_CRYPT_AES_GCM);
}
+ // Because encryption keys are sensitive and userdebug builds are used by large user pools
+ // such as beta testers, we only allow sensitive info such as keys on eng builds.
+ private static boolean isUnsafeBuild() {
+ return Build.IS_DEBUGGABLE && Build.IS_ENG;
+ }
+
@Override
public String toString() {
return new StringBuilder()
.append("{mName=")
.append(mName)
.append(", mKey=")
- .append(Build.IS_DEBUGGABLE ? HexDump.toHexString(mKey) : "<hidden>")
+ .append(isUnsafeBuild() ? HexDump.toHexString(mKey) : "<hidden>")
.append(", mTruncLenBits=")
.append(mTruncLenBits)
.append("}")
diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java
index f54ceb5..80b0af3 100644
--- a/core/java/android/net/IpSecConfig.java
+++ b/core/java/android/net/IpSecConfig.java
@@ -32,59 +32,29 @@
// MODE_TRANSPORT or MODE_TUNNEL
private int mMode = IpSecTransform.MODE_TRANSPORT;
- // Needs to be valid only for tunnel mode
// Preventing this from being null simplifies Java->Native binder
- private String mLocalAddress = "";
+ private String mSourceAddress = "";
// Preventing this from being null simplifies Java->Native binder
- private String mRemoteAddress = "";
+ private String mDestinationAddress = "";
// The underlying Network that represents the "gateway" Network
// for outbound packets. It may also be used to select packets.
private Network mNetwork;
- /**
- * This class captures the parameters that specifically apply to inbound or outbound traffic.
- */
- public static class Flow {
- // Minimum requirements for identifying a transform
- // SPI identifying the IPsec flow in packet processing
- // and a remote IP address
- private int mSpiResourceId = IpSecManager.INVALID_RESOURCE_ID;
+ // Minimum requirements for identifying a transform
+ // SPI identifying the IPsec SA in packet processing
+ // and a destination IP address
+ private int mSpiResourceId = IpSecManager.INVALID_RESOURCE_ID;
- // Encryption Algorithm
- private IpSecAlgorithm mEncryption;
+ // Encryption Algorithm
+ private IpSecAlgorithm mEncryption;
- // Authentication Algorithm
- private IpSecAlgorithm mAuthentication;
+ // Authentication Algorithm
+ private IpSecAlgorithm mAuthentication;
- // Authenticated Encryption Algorithm
- private IpSecAlgorithm mAuthenticatedEncryption;
-
- @Override
- public String toString() {
- return new StringBuilder()
- .append("{mSpiResourceId=")
- .append(mSpiResourceId)
- .append(", mEncryption=")
- .append(mEncryption)
- .append(", mAuthentication=")
- .append(mAuthentication)
- .append(", mAuthenticatedEncryption=")
- .append(mAuthenticatedEncryption)
- .append("}")
- .toString();
- }
-
- static boolean equals(IpSecConfig.Flow lhs, IpSecConfig.Flow rhs) {
- if (lhs == null || rhs == null) return (lhs == rhs);
- return (lhs.mSpiResourceId == rhs.mSpiResourceId
- && IpSecAlgorithm.equals(lhs.mEncryption, rhs.mEncryption)
- && IpSecAlgorithm.equals(lhs.mAuthentication, rhs.mAuthentication));
- }
- }
-
- private final Flow[] mFlow = new Flow[] {new Flow(), new Flow()};
+ // Authenticated Encryption Algorithm
+ private IpSecAlgorithm mAuthenticatedEncryption;
// For tunnel mode IPv4 UDP Encapsulation
// IpSecTransform#ENCAP_ESP_*, such as ENCAP_ESP_OVER_UDP_IKE
@@ -100,36 +70,37 @@
mMode = mode;
}
- /** Set the local IP address for Tunnel mode */
- public void setLocalAddress(String localAddress) {
- mLocalAddress = localAddress;
+ /** Set the source IP addres for this IPsec transform */
+ public void setSourceAddress(String sourceAddress) {
+ mSourceAddress = sourceAddress;
}
- /** Set the remote IP address for this IPsec transform */
- public void setRemoteAddress(String remoteAddress) {
- mRemoteAddress = remoteAddress;
+ /** Set the destination IP address for this IPsec transform */
+ public void setDestinationAddress(String destinationAddress) {
+ mDestinationAddress = destinationAddress;
}
- /** Set the SPI for a given direction by resource ID */
- public void setSpiResourceId(int direction, int resourceId) {
- mFlow[direction].mSpiResourceId = resourceId;
+ /** Set the SPI by resource ID */
+ public void setSpiResourceId(int resourceId) {
+ mSpiResourceId = resourceId;
}
- /** Set the encryption algorithm for a given direction */
- public void setEncryption(int direction, IpSecAlgorithm encryption) {
- mFlow[direction].mEncryption = encryption;
+ /** Set the encryption algorithm */
+ public void setEncryption(IpSecAlgorithm encryption) {
+ mEncryption = encryption;
}
- /** Set the authentication algorithm for a given direction */
- public void setAuthentication(int direction, IpSecAlgorithm authentication) {
- mFlow[direction].mAuthentication = authentication;
+ /** Set the authentication algorithm */
+ public void setAuthentication(IpSecAlgorithm authentication) {
+ mAuthentication = authentication;
}
- /** Set the authenticated encryption algorithm for a given direction */
- public void setAuthenticatedEncryption(int direction, IpSecAlgorithm authenticatedEncryption) {
- mFlow[direction].mAuthenticatedEncryption = authenticatedEncryption;
+ /** Set the authenticated encryption algorithm */
+ public void setAuthenticatedEncryption(IpSecAlgorithm authenticatedEncryption) {
+ mAuthenticatedEncryption = authenticatedEncryption;
}
+ /** Set the underlying network that will carry traffic for this transform */
public void setNetwork(Network network) {
mNetwork = network;
}
@@ -155,28 +126,28 @@
return mMode;
}
- public String getLocalAddress() {
- return mLocalAddress;
+ public String getSourceAddress() {
+ return mSourceAddress;
}
- public int getSpiResourceId(int direction) {
- return mFlow[direction].mSpiResourceId;
+ public int getSpiResourceId() {
+ return mSpiResourceId;
}
- public String getRemoteAddress() {
- return mRemoteAddress;
+ public String getDestinationAddress() {
+ return mDestinationAddress;
}
- public IpSecAlgorithm getEncryption(int direction) {
- return mFlow[direction].mEncryption;
+ public IpSecAlgorithm getEncryption() {
+ return mEncryption;
}
- public IpSecAlgorithm getAuthentication(int direction) {
- return mFlow[direction].mAuthentication;
+ public IpSecAlgorithm getAuthentication() {
+ return mAuthentication;
}
- public IpSecAlgorithm getAuthenticatedEncryption(int direction) {
- return mFlow[direction].mAuthenticatedEncryption;
+ public IpSecAlgorithm getAuthenticatedEncryption() {
+ return mAuthenticatedEncryption;
}
public Network getNetwork() {
@@ -209,17 +180,13 @@
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mMode);
- out.writeString(mLocalAddress);
- out.writeString(mRemoteAddress);
+ out.writeString(mSourceAddress);
+ out.writeString(mDestinationAddress);
out.writeParcelable(mNetwork, flags);
- out.writeInt(mFlow[IpSecTransform.DIRECTION_IN].mSpiResourceId);
- out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mEncryption, flags);
- out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mAuthentication, flags);
- out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mAuthenticatedEncryption, flags);
- out.writeInt(mFlow[IpSecTransform.DIRECTION_OUT].mSpiResourceId);
- out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mEncryption, flags);
- out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mAuthentication, flags);
- out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mAuthenticatedEncryption, flags);
+ out.writeInt(mSpiResourceId);
+ out.writeParcelable(mEncryption, flags);
+ out.writeParcelable(mAuthentication, flags);
+ out.writeParcelable(mAuthenticatedEncryption, flags);
out.writeInt(mEncapType);
out.writeInt(mEncapSocketResourceId);
out.writeInt(mEncapRemotePort);
@@ -231,22 +198,15 @@
private IpSecConfig(Parcel in) {
mMode = in.readInt();
- mLocalAddress = in.readString();
- mRemoteAddress = in.readString();
+ mSourceAddress = in.readString();
+ mDestinationAddress = in.readString();
mNetwork = (Network) in.readParcelable(Network.class.getClassLoader());
- mFlow[IpSecTransform.DIRECTION_IN].mSpiResourceId = in.readInt();
- mFlow[IpSecTransform.DIRECTION_IN].mEncryption =
+ mSpiResourceId = in.readInt();
+ mEncryption =
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
- mFlow[IpSecTransform.DIRECTION_IN].mAuthentication =
+ mAuthentication =
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
- mFlow[IpSecTransform.DIRECTION_IN].mAuthenticatedEncryption =
- (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
- mFlow[IpSecTransform.DIRECTION_OUT].mSpiResourceId = in.readInt();
- mFlow[IpSecTransform.DIRECTION_OUT].mEncryption =
- (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
- mFlow[IpSecTransform.DIRECTION_OUT].mAuthentication =
- (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
- mFlow[IpSecTransform.DIRECTION_OUT].mAuthenticatedEncryption =
+ mAuthenticatedEncryption =
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
mEncapType = in.readInt();
mEncapSocketResourceId = in.readInt();
@@ -260,10 +220,10 @@
strBuilder
.append("{mMode=")
.append(mMode == IpSecTransform.MODE_TUNNEL ? "TUNNEL" : "TRANSPORT")
- .append(", mLocalAddress=")
- .append(mLocalAddress)
- .append(", mRemoteAddress=")
- .append(mRemoteAddress)
+ .append(", mSourceAddress=")
+ .append(mSourceAddress)
+ .append(", mDestinationAddress=")
+ .append(mDestinationAddress)
.append(", mNetwork=")
.append(mNetwork)
.append(", mEncapType=")
@@ -274,10 +234,14 @@
.append(mEncapRemotePort)
.append(", mNattKeepaliveInterval=")
.append(mNattKeepaliveInterval)
- .append(", mFlow[OUT]=")
- .append(mFlow[IpSecTransform.DIRECTION_OUT])
- .append(", mFlow[IN]=")
- .append(mFlow[IpSecTransform.DIRECTION_IN])
+ .append("{mSpiResourceId=")
+ .append(mSpiResourceId)
+ .append(", mEncryption=")
+ .append(mEncryption)
+ .append(", mAuthentication=")
+ .append(mAuthentication)
+ .append(", mAuthenticatedEncryption=")
+ .append(mAuthenticatedEncryption)
.append("}");
return strBuilder.toString();
@@ -299,17 +263,18 @@
public static boolean equals(IpSecConfig lhs, IpSecConfig rhs) {
if (lhs == null || rhs == null) return (lhs == rhs);
return (lhs.mMode == rhs.mMode
- && lhs.mLocalAddress.equals(rhs.mLocalAddress)
- && lhs.mRemoteAddress.equals(rhs.mRemoteAddress)
+ && lhs.mSourceAddress.equals(rhs.mSourceAddress)
+ && lhs.mDestinationAddress.equals(rhs.mDestinationAddress)
&& ((lhs.mNetwork != null && lhs.mNetwork.equals(rhs.mNetwork))
|| (lhs.mNetwork == rhs.mNetwork))
&& lhs.mEncapType == rhs.mEncapType
&& lhs.mEncapSocketResourceId == rhs.mEncapSocketResourceId
&& lhs.mEncapRemotePort == rhs.mEncapRemotePort
&& lhs.mNattKeepaliveInterval == rhs.mNattKeepaliveInterval
- && IpSecConfig.Flow.equals(lhs.mFlow[IpSecTransform.DIRECTION_OUT],
- rhs.mFlow[IpSecTransform.DIRECTION_OUT])
- && IpSecConfig.Flow.equals(lhs.mFlow[IpSecTransform.DIRECTION_IN],
- rhs.mFlow[IpSecTransform.DIRECTION_IN]));
+ && lhs.mSpiResourceId == rhs.mSpiResourceId
+ && IpSecAlgorithm.equals(lhs.mEncryption, rhs.mEncryption)
+ && IpSecAlgorithm.equals(
+ lhs.mAuthenticatedEncryption, rhs.mAuthenticatedEncryption)
+ && IpSecAlgorithm.equals(lhs.mAuthentication, rhs.mAuthentication));
}
}
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 34cfa9b..2cda58c 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -17,6 +17,7 @@
import static com.android.internal.util.Preconditions.checkNotNull;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -33,6 +34,8 @@
import java.io.FileDescriptor;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
@@ -53,6 +56,23 @@
private static final String TAG = "IpSecManager";
/**
+ * For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
+ * applies to traffic towards the host.
+ */
+ public static final int DIRECTION_IN = 0;
+
+ /**
+ * For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
+ * applies to traffic from the host.
+ */
+ public static final int DIRECTION_OUT = 1;
+
+ /** @hide */
+ @IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PolicyDirection {}
+
+ /**
* The Security Parameter Index (SPI) 0 indicates an unknown or invalid index.
*
* <p>No IPsec packet may contain an SPI of 0.
@@ -125,7 +145,7 @@
*/
public static final class SecurityParameterIndex implements AutoCloseable {
private final IIpSecService mService;
- private final InetAddress mRemoteAddress;
+ private final InetAddress mDestinationAddress;
private final CloseGuard mCloseGuard = CloseGuard.get();
private int mSpi = INVALID_SECURITY_PARAMETER_INDEX;
private int mResourceId = INVALID_RESOURCE_ID;
@@ -164,14 +184,14 @@
}
private SecurityParameterIndex(
- @NonNull IIpSecService service, int direction, InetAddress remoteAddress, int spi)
+ @NonNull IIpSecService service, InetAddress destinationAddress, int spi)
throws ResourceUnavailableException, SpiUnavailableException {
mService = service;
- mRemoteAddress = remoteAddress;
+ mDestinationAddress = destinationAddress;
try {
IpSecSpiResponse result =
mService.allocateSecurityParameterIndex(
- direction, remoteAddress.getHostAddress(), spi, new Binder());
+ destinationAddress.getHostAddress(), spi, new Binder());
if (result == null) {
throw new NullPointerException("Received null response from IpSecService");
@@ -216,25 +236,23 @@
}
/**
- * Reserve a random SPI for traffic bound to or from the specified remote address.
+ * Reserve a random SPI for traffic bound to or from the specified destination address.
*
* <p>If successful, this SPI is guaranteed available until released by a call to {@link
* SecurityParameterIndex#close()}.
*
- * @param direction {@link IpSecTransform#DIRECTION_IN} or {@link IpSecTransform#DIRECTION_OUT}
- * @param remoteAddress address of the remote. SPIs must be unique for each remoteAddress
+ * @param destinationAddress the destination address for traffic bearing the requested SPI.
+ * For inbound traffic, the destination should be an address currently assigned on-device.
* @return the reserved SecurityParameterIndex
- * @throws ResourceUnavailableException indicating that too many SPIs are currently allocated
- * for this user
- * @throws SpiUnavailableException indicating that a particular SPI cannot be reserved
+ * @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
+ * currently allocated for this user
*/
- public SecurityParameterIndex allocateSecurityParameterIndex(
- int direction, InetAddress remoteAddress) throws ResourceUnavailableException {
+ public SecurityParameterIndex allocateSecurityParameterIndex(InetAddress destinationAddress)
+ throws ResourceUnavailableException {
try {
return new SecurityParameterIndex(
mService,
- direction,
- remoteAddress,
+ destinationAddress,
IpSecManager.INVALID_SECURITY_PARAMETER_INDEX);
} catch (SpiUnavailableException unlikely) {
throw new ResourceUnavailableException("No SPIs available");
@@ -242,26 +260,27 @@
}
/**
- * Reserve the requested SPI for traffic bound to or from the specified remote address.
+ * Reserve the requested SPI for traffic bound to or from the specified destination address.
*
* <p>If successful, this SPI is guaranteed available until released by a call to {@link
* SecurityParameterIndex#close()}.
*
- * @param direction {@link IpSecTransform#DIRECTION_IN} or {@link IpSecTransform#DIRECTION_OUT}
- * @param remoteAddress address of the remote. SPIs must be unique for each remoteAddress
+ * @param destinationAddress the destination address for traffic bearing the requested SPI.
+ * For inbound traffic, the destination should be an address currently assigned on-device.
* @param requestedSpi the requested SPI, or '0' to allocate a random SPI
* @return the reserved SecurityParameterIndex
- * @throws ResourceUnavailableException indicating that too many SPIs are currently allocated
- * for this user
- * @throws SpiUnavailableException indicating that the requested SPI could not be reserved
+ * @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
+ * currently allocated for this user
+ * @throws {@link #SpiUnavailableException} indicating that the requested SPI could not be
+ * reserved
*/
public SecurityParameterIndex allocateSecurityParameterIndex(
- int direction, InetAddress remoteAddress, int requestedSpi)
+ InetAddress destinationAddress, int requestedSpi)
throws SpiUnavailableException, ResourceUnavailableException {
if (requestedSpi == IpSecManager.INVALID_SECURITY_PARAMETER_INDEX) {
throw new IllegalArgumentException("Requested SPI must be a valid (non-zero) SPI");
}
- return new SecurityParameterIndex(mService, direction, remoteAddress, requestedSpi);
+ return new SecurityParameterIndex(mService, destinationAddress, requestedSpi);
}
/**
@@ -269,14 +288,14 @@
*
* <p>This applies transport mode encapsulation to the given socket. Once applied, I/O on the
* socket will be encapsulated according to the parameters of the {@code IpSecTransform}. When
- * the transform is removed from the socket by calling {@link #removeTransportModeTransform},
+ * the transform is removed from the socket by calling {@link #removeTransportModeTransforms},
* unprotected traffic can resume on that socket.
*
* <p>For security reasons, the destination address of any traffic on the socket must match the
* remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any
* other IP address will result in an IOException. In addition, reads and writes on the socket
* will throw IOException if the user deactivates the transform (by calling {@link
- * IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}.
+ * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
*
* <h4>Rekey Procedure</h4>
*
@@ -287,15 +306,14 @@
* in-flight packets have been received.
*
* @param socket a stream socket
+ * @param direction the policy direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
* @param transform a transport mode {@code IpSecTransform}
* @throws IOException indicating that the transform could not be applied
- * @hide
*/
- public void applyTransportModeTransform(Socket socket, IpSecTransform transform)
+ public void applyTransportModeTransform(
+ Socket socket, int direction, IpSecTransform transform)
throws IOException {
- try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket)) {
- applyTransportModeTransform(pfd, transform);
- }
+ applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform);
}
/**
@@ -303,14 +321,14 @@
*
* <p>This applies transport mode encapsulation to the given socket. Once applied, I/O on the
* socket will be encapsulated according to the parameters of the {@code IpSecTransform}. When
- * the transform is removed from the socket by calling {@link #removeTransportModeTransform},
+ * the transform is removed from the socket by calling {@link #removeTransportModeTransforms},
* unprotected traffic can resume on that socket.
*
* <p>For security reasons, the destination address of any traffic on the socket must match the
* remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any
* other IP address will result in an IOException. In addition, reads and writes on the socket
* will throw IOException if the user deactivates the transform (by calling {@link
- * IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}.
+ * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
*
* <h4>Rekey Procedure</h4>
*
@@ -321,15 +339,13 @@
* in-flight packets have been received.
*
* @param socket a datagram socket
+ * @param direction the policy direction either DIRECTION_IN or DIRECTION_OUT
* @param transform a transport mode {@code IpSecTransform}
* @throws IOException indicating that the transform could not be applied
- * @hide
*/
- public void applyTransportModeTransform(DatagramSocket socket, IpSecTransform transform)
- throws IOException {
- try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket)) {
- applyTransportModeTransform(pfd, transform);
- }
+ public void applyTransportModeTransform(
+ DatagramSocket socket, int direction, IpSecTransform transform) throws IOException {
+ applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform);
}
/**
@@ -337,14 +353,14 @@
*
* <p>This applies transport mode encapsulation to the given socket. Once applied, I/O on the
* socket will be encapsulated according to the parameters of the {@code IpSecTransform}. When
- * the transform is removed from the socket by calling {@link #removeTransportModeTransform},
+ * the transform is removed from the socket by calling {@link #removeTransportModeTransforms},
* unprotected traffic can resume on that socket.
*
* <p>For security reasons, the destination address of any traffic on the socket must match the
* remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any
* other IP address will result in an IOException. In addition, reads and writes on the socket
* will throw IOException if the user deactivates the transform (by calling {@link
- * IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}.
+ * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
*
* <h4>Rekey Procedure</h4>
*
@@ -355,24 +371,17 @@
* in-flight packets have been received.
*
* @param socket a socket file descriptor
+ * @param direction the policy direction either DIRECTION_IN or DIRECTION_OUT
* @param transform a transport mode {@code IpSecTransform}
* @throws IOException indicating that the transform could not be applied
*/
- public void applyTransportModeTransform(FileDescriptor socket, IpSecTransform transform)
+ public void applyTransportModeTransform(
+ FileDescriptor socket, int direction, IpSecTransform transform)
throws IOException {
// We dup() the FileDescriptor here because if we don't, then the ParcelFileDescriptor()
- // constructor takes control and closes the user's FD when we exit the method
- // This is behaviorally the same as the other versions, but the PFD constructor does not
- // dup() automatically, whereas PFD.fromSocket() and PDF.fromDatagramSocket() do dup().
+ // constructor takes control and closes the user's FD when we exit the method.
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
- applyTransportModeTransform(pfd, transform);
- }
- }
-
- /* Call down to activate a transform */
- private void applyTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {
- try {
- mService.applyTransportModeTransform(pfd, transform.getResourceId());
+ mService.applyTransportModeTransform(pfd, direction, transform.getResourceId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -396,75 +405,56 @@
/**
* Remove an IPsec transform from a stream socket.
*
- * <p>Once removed, traffic on the socket will not be encrypted. This operation will succeed
- * regardless of the state of the transform. Removing a transform from a socket allows the
- * socket to be reused for communication in the clear.
+ * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a
+ * socket allows the socket to be reused for communication in the clear.
*
* <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling
* {@link IpSecTransform#close()}, then communication on the socket will fail until this method
* is called.
*
* @param socket a socket that previously had a transform applied to it
- * @param transform the IPsec Transform that was previously applied to the given socket
* @throws IOException indicating that the transform could not be removed from the socket
- * @hide
*/
- public void removeTransportModeTransform(Socket socket, IpSecTransform transform)
+ public void removeTransportModeTransforms(Socket socket)
throws IOException {
- try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket)) {
- removeTransportModeTransform(pfd, transform);
- }
+ removeTransportModeTransforms(socket.getFileDescriptor$());
}
/**
* Remove an IPsec transform from a datagram socket.
*
- * <p>Once removed, traffic on the socket will not be encrypted. This operation will succeed
- * regardless of the state of the transform. Removing a transform from a socket allows the
- * socket to be reused for communication in the clear.
+ * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a
+ * socket allows the socket to be reused for communication in the clear.
*
* <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling
* {@link IpSecTransform#close()}, then communication on the socket will fail until this method
* is called.
*
* @param socket a socket that previously had a transform applied to it
- * @param transform the IPsec Transform that was previously applied to the given socket
* @throws IOException indicating that the transform could not be removed from the socket
- * @hide
*/
- public void removeTransportModeTransform(DatagramSocket socket, IpSecTransform transform)
+ public void removeTransportModeTransforms(DatagramSocket socket)
throws IOException {
- try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket)) {
- removeTransportModeTransform(pfd, transform);
- }
+ removeTransportModeTransforms(socket.getFileDescriptor$());
}
/**
* Remove an IPsec transform from a socket.
*
- * <p>Once removed, traffic on the socket will not be encrypted. This operation will succeed
- * regardless of the state of the transform. Removing a transform from a socket allows the
- * socket to be reused for communication in the clear.
+ * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a
+ * socket allows the socket to be reused for communication in the clear.
*
* <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling
* {@link IpSecTransform#close()}, then communication on the socket will fail until this method
* is called.
*
* @param socket a socket that previously had a transform applied to it
- * @param transform the IPsec Transform that was previously applied to the given socket
* @throws IOException indicating that the transform could not be removed from the socket
*/
- public void removeTransportModeTransform(FileDescriptor socket, IpSecTransform transform)
+ public void removeTransportModeTransforms(FileDescriptor socket)
throws IOException {
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
- removeTransportModeTransform(pfd, transform);
- }
- }
-
- /* Call down to remove a transform */
- private void removeTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {
- try {
- mService.removeTransportModeTransform(pfd, transform.getResourceId());
+ mService.removeTransportModeTransforms(pfd);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 102ba6d..7b9b483 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -38,13 +38,11 @@
import java.net.InetAddress;
/**
- * This class represents an IPsec transform, which comprises security associations in one or both
- * directions.
+ * This class represents a transform, which roughly corresponds to an IPsec Security Association.
*
* <p>Transforms are created using {@link IpSecTransform.Builder}. Each {@code IpSecTransform}
- * object encapsulates the properties and state of an inbound and outbound IPsec security
- * association. That includes, but is not limited to, algorithm choice, key material, and allocated
- * system resources.
+ * object encapsulates the properties and state of an IPsec security association. That includes,
+ * but is not limited to, algorithm choice, key material, and allocated system resources.
*
* @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the
* Internet Protocol</a>
@@ -52,23 +50,6 @@
public final class IpSecTransform implements AutoCloseable {
private static final String TAG = "IpSecTransform";
- /**
- * For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
- * applies to traffic towards the host.
- */
- public static final int DIRECTION_IN = 0;
-
- /**
- * For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
- * applies to traffic from the host.
- */
- public static final int DIRECTION_OUT = 1;
-
- /** @hide */
- @IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
- @Retention(RetentionPolicy.SOURCE)
- public @interface TransformDirection {}
-
/** @hide */
public static final int MODE_TRANSPORT = 0;
@@ -170,7 +151,7 @@
*
* <p>Deactivating a transform while it is still applied to a socket will result in errors on
* that socket. Make sure to remove transforms by calling {@link
- * IpSecManager#removeTransportModeTransform}. Note, removing an {@code IpSecTransform} from a
+ * IpSecManager#removeTransportModeTransforms}. Note, removing an {@code IpSecTransform} from a
* socket will not deactivate it (because one transform may be applied to multiple sockets).
*
* <p>It is safe to call this method on a transform that has already been deactivated.
@@ -272,85 +253,49 @@
private IpSecConfig mConfig;
/**
- * Set the encryption algorithm for the given direction.
- *
- * <p>If encryption is set for a direction without also providing an SPI for that direction,
- * creation of an {@code IpSecTransform} will fail when attempting to build the transform.
+ * Set the encryption algorithm.
*
* <p>Encryption is mutually exclusive with authenticated encryption.
*
- * @param direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
* @param algo {@link IpSecAlgorithm} specifying the encryption to be applied.
*/
- public IpSecTransform.Builder setEncryption(
- @TransformDirection int direction, IpSecAlgorithm algo) {
+ public IpSecTransform.Builder setEncryption(@NonNull IpSecAlgorithm algo) {
// TODO: throw IllegalArgumentException if algo is not an encryption algorithm.
- mConfig.setEncryption(direction, algo);
+ Preconditions.checkNotNull(algo);
+ mConfig.setEncryption(algo);
return this;
}
/**
- * Set the authentication (integrity) algorithm for the given direction.
- *
- * <p>If authentication is set for a direction without also providing an SPI for that
- * direction, creation of an {@code IpSecTransform} will fail when attempting to build the
- * transform.
+ * Set the authentication (integrity) algorithm.
*
* <p>Authentication is mutually exclusive with authenticated encryption.
*
- * @param direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
* @param algo {@link IpSecAlgorithm} specifying the authentication to be applied.
*/
- public IpSecTransform.Builder setAuthentication(
- @TransformDirection int direction, IpSecAlgorithm algo) {
+ public IpSecTransform.Builder setAuthentication(@NonNull IpSecAlgorithm algo) {
// TODO: throw IllegalArgumentException if algo is not an authentication algorithm.
- mConfig.setAuthentication(direction, algo);
+ Preconditions.checkNotNull(algo);
+ mConfig.setAuthentication(algo);
return this;
}
/**
- * Set the authenticated encryption algorithm for the given direction.
+ * Set the authenticated encryption algorithm.
*
- * <p>If an authenticated encryption algorithm is set for a given direction without also
- * providing an SPI for that direction, creation of an {@code IpSecTransform} will fail when
- * attempting to build the transform.
- *
- * <p>The Authenticated Encryption (AE) class of algorithms are also known as Authenticated
- * Encryption with Associated Data (AEAD) algorithms, or Combined mode algorithms (as
- * referred to in <a href="https://tools.ietf.org/html/rfc4301">RFC 4301</a>).
+ * <p>The Authenticated Encryption (AE) class of algorithms are also known as
+ * Authenticated Encryption with Associated Data (AEAD) algorithms, or Combined mode
+ * algorithms (as referred to in
+ * <a href="https://tools.ietf.org/html/rfc4301">RFC 4301</a>).
*
* <p>Authenticated encryption is mutually exclusive with encryption and authentication.
*
- * @param direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
* @param algo {@link IpSecAlgorithm} specifying the authenticated encryption algorithm to
* be applied.
*/
- public IpSecTransform.Builder setAuthenticatedEncryption(
- @TransformDirection int direction, IpSecAlgorithm algo) {
- mConfig.setAuthenticatedEncryption(direction, algo);
- return this;
- }
-
- /**
- * Set the SPI for the given direction.
- *
- * <p>Because IPsec operates at the IP layer, this 32-bit identifier uniquely identifies
- * packets to a given destination address. To prevent SPI collisions, values should be
- * reserved by calling {@link IpSecManager#allocateSecurityParameterIndex}.
- *
- * <p>If the SPI and algorithms are omitted for one direction, traffic in that direction
- * will not be encrypted or authenticated.
- *
- * @param direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
- * @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
- * traffic
- */
- public IpSecTransform.Builder setSpi(
- @TransformDirection int direction, IpSecManager.SecurityParameterIndex spi) {
- if (spi.getResourceId() == INVALID_RESOURCE_ID) {
- throw new IllegalArgumentException("Invalid SecurityParameterIndex");
- }
- mConfig.setSpiResourceId(direction, spi.getResourceId());
+ public IpSecTransform.Builder setAuthenticatedEncryption(@NonNull IpSecAlgorithm algo) {
+ Preconditions.checkNotNull(algo);
+ mConfig.setAuthenticatedEncryption(algo);
return this;
}
@@ -363,7 +308,8 @@
* @hide
*/
@SystemApi
- public IpSecTransform.Builder setUnderlyingNetwork(Network net) {
+ public IpSecTransform.Builder setUnderlyingNetwork(@NonNull Network net) {
+ Preconditions.checkNotNull(net);
mConfig.setNetwork(net);
return this;
}
@@ -382,7 +328,8 @@
* encapsulated traffic. In the case of IKEv2, this should be port 4500.
*/
public IpSecTransform.Builder setIpv4Encapsulation(
- IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
+ @NonNull IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
+ Preconditions.checkNotNull(localSocket);
mConfig.setEncapType(ENCAP_ESPINUDP);
if (localSocket.getResourceId() == INVALID_RESOURCE_ID) {
throw new IllegalArgumentException("Invalid UdpEncapsulationSocket");
@@ -419,24 +366,33 @@
* will not affect any network traffic until it has been applied to one or more sockets.
*
* @see IpSecManager#applyTransportModeTransform
- * @param remoteAddress the remote {@code InetAddress} of traffic on sockets that will use
- * this transform
+ * @param sourceAddress the source {@code InetAddress} of traffic on sockets that will use
+ * this transform; this address must belong to the Network used by all sockets that
+ * utilize this transform; if provided, then only traffic originating from the
+ * specified source address will be processed.
+ * @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
+ * traffic
* @throws IllegalArgumentException indicating that a particular combination of transform
* properties is invalid
- * @throws IpSecManager.ResourceUnavailableException indicating that too many transforms are
- * active
+ * @throws IpSecManager.ResourceUnavailableException indicating that too many transforms
+ * are active
* @throws IpSecManager.SpiUnavailableException indicating the rare case where an SPI
* collides with an existing transform
* @throws IOException indicating other errors
*/
- public IpSecTransform buildTransportModeTransform(InetAddress remoteAddress)
+ public IpSecTransform buildTransportModeTransform(
+ @NonNull InetAddress sourceAddress,
+ @NonNull IpSecManager.SecurityParameterIndex spi)
throws IpSecManager.ResourceUnavailableException,
IpSecManager.SpiUnavailableException, IOException {
- if (remoteAddress == null) {
- throw new IllegalArgumentException("Remote address may not be null or empty!");
+ Preconditions.checkNotNull(sourceAddress);
+ Preconditions.checkNotNull(spi);
+ if (spi.getResourceId() == INVALID_RESOURCE_ID) {
+ throw new IllegalArgumentException("Invalid SecurityParameterIndex");
}
mConfig.setMode(MODE_TRANSPORT);
- mConfig.setRemoteAddress(remoteAddress.getHostAddress());
+ mConfig.setSourceAddress(sourceAddress.getHostAddress());
+ mConfig.setSpiResourceId(spi.getResourceId());
// FIXME: modifying a builder after calling build can change the built transform.
return new IpSecTransform(mContext, mConfig).activate();
}
@@ -445,26 +401,33 @@
* Build and return an {@link IpSecTransform} object as a Tunnel Mode Transform. Some
* parameters have interdependencies that are checked at build time.
*
- * @param localAddress the {@link InetAddress} that provides the local endpoint for this
+ * @param sourceAddress the {@link InetAddress} that provides the source address for this
* IPsec tunnel. This is almost certainly an address belonging to the {@link Network}
* that will originate the traffic, which is set as the {@link #setUnderlyingNetwork}.
- * @param remoteAddress the {@link InetAddress} representing the remote endpoint of this
- * IPsec tunnel.
+ * @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
+ * traffic
* @throws IllegalArgumentException indicating that a particular combination of transform
* properties is invalid.
+ * @throws IpSecManager.ResourceUnavailableException indicating that too many transforms
+ * are active
+ * @throws IpSecManager.SpiUnavailableException indicating the rare case where an SPI
+ * collides with an existing transform
+ * @throws IOException indicating other errors
* @hide
*/
public IpSecTransform buildTunnelModeTransform(
- InetAddress localAddress, InetAddress remoteAddress) {
- if (localAddress == null) {
- throw new IllegalArgumentException("Local address may not be null or empty!");
+ @NonNull InetAddress sourceAddress,
+ @NonNull IpSecManager.SecurityParameterIndex spi)
+ throws IpSecManager.ResourceUnavailableException,
+ IpSecManager.SpiUnavailableException, IOException {
+ Preconditions.checkNotNull(sourceAddress);
+ Preconditions.checkNotNull(spi);
+ if (spi.getResourceId() == INVALID_RESOURCE_ID) {
+ throw new IllegalArgumentException("Invalid SecurityParameterIndex");
}
- if (remoteAddress == null) {
- throw new IllegalArgumentException("Remote address may not be null or empty!");
- }
- mConfig.setLocalAddress(localAddress.getHostAddress());
- mConfig.setRemoteAddress(remoteAddress.getHostAddress());
mConfig.setMode(MODE_TUNNEL);
+ mConfig.setSourceAddress(sourceAddress.getHostAddress());
+ mConfig.setSpiResourceId(spi.getResourceId());
return new IpSecTransform(mContext, mConfig);
}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 4e474c8..f525b1f 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -50,6 +50,8 @@
private String mIfaceName;
private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>();
private ArrayList<InetAddress> mDnses = new ArrayList<InetAddress>();
+ private boolean mUsePrivateDns;
+ private String mPrivateDnsServerName;
private String mDomains;
private ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
private ProxyInfo mHttpProxy;
@@ -165,6 +167,8 @@
mIfaceName = source.getInterfaceName();
for (LinkAddress l : source.getLinkAddresses()) mLinkAddresses.add(l);
for (InetAddress i : source.getDnsServers()) mDnses.add(i);
+ mUsePrivateDns = source.mUsePrivateDns;
+ mPrivateDnsServerName = source.mPrivateDnsServerName;
mDomains = source.getDomains();
for (RouteInfo r : source.getRoutes()) mRoutes.add(r);
mHttpProxy = (source.getHttpProxy() == null) ?
@@ -391,6 +395,59 @@
}
/**
+ * Set whether private DNS is currently in use on this network.
+ *
+ * @param usePrivateDns The private DNS state.
+ * @hide
+ */
+ public void setUsePrivateDns(boolean usePrivateDns) {
+ mUsePrivateDns = usePrivateDns;
+ }
+
+ /**
+ * Returns whether private DNS is currently in use on this network. When
+ * private DNS is in use, applications must not send unencrypted DNS
+ * queries as doing so could reveal private user information. Furthermore,
+ * if private DNS is in use and {@link #getPrivateDnsServerName} is not
+ * {@code null}, DNS queries must be sent to the specified DNS server.
+ *
+ * @return {@code true} if private DNS is in use, {@code false} otherwise.
+ */
+ public boolean isPrivateDnsActive() {
+ return mUsePrivateDns;
+ }
+
+ /**
+ * Set the name of the private DNS server to which private DNS queries
+ * should be sent when in strict mode. This value should be {@code null}
+ * when private DNS is off or in opportunistic mode.
+ *
+ * @param privateDnsServerName The private DNS server name.
+ * @hide
+ */
+ public void setPrivateDnsServerName(@Nullable String privateDnsServerName) {
+ mPrivateDnsServerName = privateDnsServerName;
+ }
+
+ /**
+ * Returns the private DNS server name that is in use. If not {@code null},
+ * private DNS is in strict mode. In this mode, applications should ensure
+ * that all DNS queries are encrypted and sent to this hostname and that
+ * queries are only sent if the hostname's certificate is valid. If
+ * {@code null} and {@link #isPrivateDnsActive} is {@code true}, private
+ * DNS is in opportunistic mode, and applications should ensure that DNS
+ * queries are encrypted and sent to a DNS server returned by
+ * {@link #getDnsServers}. System DNS will handle each of these cases
+ * correctly, but applications implementing their own DNS lookups must make
+ * sure to follow these requirements.
+ *
+ * @return The private DNS server name.
+ */
+ public @Nullable String getPrivateDnsServerName() {
+ return mPrivateDnsServerName;
+ }
+
+ /**
* Sets the DNS domain search path used on this link.
*
* @param domains A {@link String} listing in priority order the comma separated
@@ -622,6 +679,8 @@
mIfaceName = null;
mLinkAddresses.clear();
mDnses.clear();
+ mUsePrivateDns = false;
+ mPrivateDnsServerName = null;
mDomains = null;
mRoutes.clear();
mHttpProxy = null;
@@ -649,6 +708,13 @@
for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ",";
dns += "] ";
+ String usePrivateDns = "UsePrivateDns: " + mUsePrivateDns + " ";
+
+ String privateDnsServerName = "";
+ if (privateDnsServerName != null) {
+ privateDnsServerName = "PrivateDnsServerName: " + mPrivateDnsServerName + " ";
+ }
+
String domainName = "Domains: " + mDomains;
String mtu = " MTU: " + mMtu;
@@ -671,8 +737,9 @@
}
stacked += "] ";
}
- return "{" + ifaceName + linkAddresses + routes + dns + domainName + mtu
- + tcpBuffSizes + proxy + stacked + "}";
+ return "{" + ifaceName + linkAddresses + routes + dns + usePrivateDns
+ + privateDnsServerName + domainName + mtu + tcpBuffSizes + proxy
+ + stacked + "}";
}
/**
@@ -896,6 +963,20 @@
}
/**
+ * Compares this {@code LinkProperties} private DNS settings against the
+ * target.
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isIdenticalPrivateDns(LinkProperties target) {
+ return (isPrivateDnsActive() == target.isPrivateDnsActive()
+ && TextUtils.equals(getPrivateDnsServerName(),
+ target.getPrivateDnsServerName()));
+ }
+
+ /**
* Compares this {@code LinkProperties} Routes against the target
*
* @param target LinkProperties to compare.
@@ -989,14 +1070,15 @@
* stacked interfaces are not so much a property of the link as a
* description of connections between links.
*/
- return isIdenticalInterfaceName(target) &&
- isIdenticalAddresses(target) &&
- isIdenticalDnses(target) &&
- isIdenticalRoutes(target) &&
- isIdenticalHttpProxy(target) &&
- isIdenticalStackedLinks(target) &&
- isIdenticalMtu(target) &&
- isIdenticalTcpBufferSizes(target);
+ return isIdenticalInterfaceName(target)
+ && isIdenticalAddresses(target)
+ && isIdenticalDnses(target)
+ && isIdenticalPrivateDns(target)
+ && isIdenticalRoutes(target)
+ && isIdenticalHttpProxy(target)
+ && isIdenticalStackedLinks(target)
+ && isIdenticalMtu(target)
+ && isIdenticalTcpBufferSizes(target);
}
/**
@@ -1091,7 +1173,9 @@
+ ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode())
+ mStackedLinks.hashCode() * 47)
+ mMtu * 51
- + ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode());
+ + ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode())
+ + (mUsePrivateDns ? 57 : 0)
+ + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode());
}
/**
@@ -1108,6 +1192,8 @@
for(InetAddress d : mDnses) {
dest.writeByteArray(d.getAddress());
}
+ dest.writeBoolean(mUsePrivateDns);
+ dest.writeString(mPrivateDnsServerName);
dest.writeString(mDomains);
dest.writeInt(mMtu);
dest.writeString(mTcpBufferSizes);
@@ -1148,6 +1234,8 @@
netProp.addDnsServer(InetAddress.getByAddress(in.createByteArray()));
} catch (UnknownHostException e) { }
}
+ netProp.setUsePrivateDns(in.readBoolean());
+ netProp.setPrivateDnsServerName(in.readString());
netProp.setDomains(in.readString());
netProp.setMtu(in.readInt());
netProp.setTcpBufferSizes(in.readString());
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index d6992aa..287bdc8 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
@@ -60,7 +61,7 @@
})
public @interface MacAddressType { }
- /** Indicates a MAC address of unknown type. */
+ /** @hide Indicates a MAC address of unknown type. */
public static final int TYPE_UNKNOWN = 0;
/** Indicates a MAC address is a unicast address. */
public static final int TYPE_UNICAST = 1;
@@ -92,7 +93,7 @@
*
* @return the int constant representing the MAC address type of this MacAddress.
*/
- public @MacAddressType int addressType() {
+ public @MacAddressType int getAddressType() {
if (equals(BROADCAST_ADDRESS)) {
return TYPE_BROADCAST;
}
@@ -120,12 +121,12 @@
/**
* @return a byte array representation of this MacAddress.
*/
- public byte[] toByteArray() {
+ public @NonNull byte[] toByteArray() {
return byteAddrFromLongAddr(mAddr);
}
@Override
- public String toString() {
+ public @NonNull String toString() {
return stringAddrFromLongAddr(mAddr);
}
@@ -133,7 +134,7 @@
* @return a String representation of the OUI part of this MacAddress made of 3 hexadecimal
* numbers in [0,ff] joined by ':' characters.
*/
- public String toOuiString() {
+ public @NonNull String toOuiString() {
return String.format(
"%02x:%02x:%02x", (mAddr >> 40) & 0xff, (mAddr >> 32) & 0xff, (mAddr >> 24) & 0xff);
}
@@ -197,7 +198,7 @@
if (!isMacAddress(addr)) {
return TYPE_UNKNOWN;
}
- return MacAddress.fromBytes(addr).addressType();
+ return MacAddress.fromBytes(addr).getAddressType();
}
/**
@@ -211,7 +212,7 @@
*
* @hide
*/
- public static byte[] byteAddrFromStringAddr(String addr) {
+ public static @NonNull byte[] byteAddrFromStringAddr(String addr) {
Preconditions.checkNotNull(addr);
String[] parts = addr.split(":");
if (parts.length != ETHER_ADDR_LEN) {
@@ -239,7 +240,7 @@
*
* @hide
*/
- public static String stringAddrFromByteAddr(byte[] addr) {
+ public static @NonNull String stringAddrFromByteAddr(byte[] addr) {
if (!isMacAddress(addr)) {
return null;
}
@@ -291,7 +292,7 @@
// Internal conversion function equivalent to stringAddrFromByteAddr(byteAddrFromLongAddr(addr))
// that avoids the allocation of an intermediary byte[].
- private static String stringAddrFromLongAddr(long addr) {
+ private static @NonNull String stringAddrFromLongAddr(long addr) {
return String.format("%02x:%02x:%02x:%02x:%02x:%02x",
(addr >> 40) & 0xff,
(addr >> 32) & 0xff,
@@ -310,7 +311,7 @@
* @return the MacAddress corresponding to the given String representation.
* @throws IllegalArgumentException if the given String is not a valid representation.
*/
- public static MacAddress fromString(String addr) {
+ public static @NonNull MacAddress fromString(@NonNull String addr) {
return new MacAddress(longAddrFromStringAddr(addr));
}
@@ -322,7 +323,7 @@
* @return the MacAddress corresponding to the given byte array representation.
* @throws IllegalArgumentException if the given byte array is not a valid representation.
*/
- public static MacAddress fromBytes(byte[] addr) {
+ public static @NonNull MacAddress fromBytes(@NonNull byte[] addr) {
return new MacAddress(longAddrFromByteAddr(addr));
}
@@ -336,7 +337,7 @@
*
* @hide
*/
- public static MacAddress createRandomUnicastAddress() {
+ public static @NonNull MacAddress createRandomUnicastAddress() {
return createRandomUnicastAddress(BASE_GOOGLE_MAC, new Random());
}
@@ -352,7 +353,7 @@
*
* @hide
*/
- public static MacAddress createRandomUnicastAddress(MacAddress base, Random r) {
+ public static @NonNull MacAddress createRandomUnicastAddress(MacAddress base, Random r) {
long addr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong());
addr = addr | LOCALLY_ASSIGNED_MASK;
addr = addr & ~MULTICAST_MASK;
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 1a3ce91..5df168d 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -357,13 +357,13 @@
// Multiple Provisioning Domains API recommendations, as made by the
// IETF mif working group.
//
- // The HANDLE_MAGIC value MUST be kept in sync with the corresponding
+ // The handleMagic value MUST be kept in sync with the corresponding
// value in the native/android/net.c NDK implementation.
if (netId == 0) {
return 0L; // make this zero condition obvious for debugging
}
- final long HANDLE_MAGIC = 0xfacade;
- return (((long) netId) << 32) | HANDLE_MAGIC;
+ final long handleMagic = 0xcafed00dL;
+ return (((long) netId) << 32) | handleMagic;
}
// implement the Parcelable interface
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 8b03fa8..214ff64 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -108,6 +108,7 @@
NET_CAPABILITY_CAPTIVE_PORTAL,
NET_CAPABILITY_NOT_ROAMING,
NET_CAPABILITY_FOREGROUND,
+ NET_CAPABILITY_NOT_CONGESTED,
})
public @interface NetCapability { }
@@ -235,8 +236,17 @@
*/
public static final int NET_CAPABILITY_FOREGROUND = 19;
+ /**
+ * Indicates that this network is not congested.
+ * <p>
+ * When a network is congested, the device should defer network traffic that
+ * can be done at a later time without breaking developer contracts.
+ * @hide
+ */
+ public static final int NET_CAPABILITY_NOT_CONGESTED = 20;
+
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
- private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_FOREGROUND;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_CONGESTED;
/**
* Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -249,7 +259,8 @@
(1 << NET_CAPABILITY_VALIDATED) |
(1 << NET_CAPABILITY_CAPTIVE_PORTAL) |
(1 << NET_CAPABILITY_NOT_ROAMING) |
- (1 << NET_CAPABILITY_FOREGROUND);
+ (1 << NET_CAPABILITY_FOREGROUND) |
+ (1 << NET_CAPABILITY_NOT_CONGESTED);
/**
* Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -387,12 +398,9 @@
* @hide
*/
public String describeFirstNonRequestableCapability() {
- if (hasCapability(NET_CAPABILITY_VALIDATED)) return "NET_CAPABILITY_VALIDATED";
- if (hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) return "NET_CAPABILITY_CAPTIVE_PORTAL";
- if (hasCapability(NET_CAPABILITY_FOREGROUND)) return "NET_CAPABILITY_FOREGROUND";
- // This cannot happen unless the preceding checks are incomplete.
- if ((mNetworkCapabilities & NON_REQUESTABLE_CAPABILITIES) != 0) {
- return "unknown non-requestable capabilities " + Long.toHexString(mNetworkCapabilities);
+ final long nonRequestable = (mNetworkCapabilities & NON_REQUESTABLE_CAPABILITIES);
+ if (nonRequestable != 0) {
+ return capabilityNameOf(BitUtils.unpackBits(nonRequestable)[0]);
}
if (mLinkUpBandwidthKbps != 0 || mLinkDownBandwidthKbps != 0) return "link bandwidth";
if (hasSignalStrength()) return "signalStrength";
@@ -1080,6 +1088,7 @@
case NET_CAPABILITY_CAPTIVE_PORTAL: return "CAPTIVE_PORTAL";
case NET_CAPABILITY_NOT_ROAMING: return "NOT_ROAMING";
case NET_CAPABILITY_FOREGROUND: return "FOREGROUND";
+ case NET_CAPABILITY_NOT_CONGESTED: return "NOT_CONGESTED";
default: return Integer.toString(capability);
}
}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 81c49a3..9ef26a9 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -29,7 +29,6 @@
import android.net.wifi.WifiInfo;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.telephony.SubscriptionPlan;
import android.util.DebugUtils;
import android.util.Pair;
@@ -329,7 +328,7 @@
* to access network when the device is idle or in battery saver mode. Otherwise, false.
*/
public static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(int procState) {
- return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+ return procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
}
/**
@@ -337,7 +336,7 @@
* to access network when the device is in data saver mode. Otherwise, false.
*/
public static boolean isProcStateAllowedWhileOnRestrictBackground(int procState) {
- return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+ return procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
}
public static String resolveNetworkId(WifiConfiguration config) {
diff --git a/core/java/android/net/metrics/WakeupStats.java b/core/java/android/net/metrics/WakeupStats.java
index 7277ba3..bb36536 100644
--- a/core/java/android/net/metrics/WakeupStats.java
+++ b/core/java/android/net/metrics/WakeupStats.java
@@ -80,7 +80,7 @@
break;
}
- switch (ev.dstHwAddr.addressType()) {
+ switch (ev.dstHwAddr.getAddressType()) {
case MacAddress.TYPE_UNICAST:
l2UnicastCount++;
break;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 77e4808..dc271d8 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -235,8 +235,11 @@
* New in version 29:
* - Process states re-ordered. TOP_SLEEPING now below BACKGROUND. HEAVY_WEIGHT introduced.
* - CPU times per UID process state
+ * New in version 30:
+ * - Uid.PROCESS_STATE_FOREGROUND_SERVICE only tracks
+ * ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE.
*/
- static final int CHECKIN_VERSION = 29;
+ static final int CHECKIN_VERSION = 30;
/**
* Old version, we hit 9 and ran out of room, need to remove.
@@ -694,17 +697,17 @@
// total time a uid has had any processes running at all.
/**
- * Time this uid has any processes in the top state (or above such as persistent).
+ * Time this uid has any processes in the top state.
*/
public static final int PROCESS_STATE_TOP = 0;
/**
- * Time this uid has any process with a started out bound foreground service, but
+ * Time this uid has any process with a started foreground service, but
* none in the "top" state.
*/
public static final int PROCESS_STATE_FOREGROUND_SERVICE = 1;
/**
* Time this uid has any process in an active foreground state, but none in the
- * "top sleeping" or better state.
+ * "foreground service" or better state. Persistent and other foreground states go here.
*/
public static final int PROCESS_STATE_FOREGROUND = 2;
/**
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index a474b47..a5e1934 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -339,7 +339,8 @@
/**
* Configure name servers, search paths, and resolver parameters for the given network.
*/
- void setDnsConfigurationForNetwork(int netId, in String[] servers, String domains);
+ void setDnsConfigurationForNetwork(int netId, in String[] servers, in String[] domains,
+ in int[] params, boolean useTls, String tlsHostname);
void setFirewallEnabled(boolean enabled);
boolean isFirewallEnabled();
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 01d6b02..65e9473 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -97,7 +97,7 @@
boolean isUserRunning(int userId);
boolean isUserNameSet(int userHandle);
boolean hasRestrictedProfiles();
- boolean trySetQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userHandle, in IntentSender target);
+ boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userHandle, in IntentSender target);
long getUserStartRealtime();
long getUserUnlockRealtime();
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index cd6d41b..3798a5e 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -565,6 +565,42 @@
int OPTIONAL_SENSORS = 13;
}
+ /**
+ * Either the location providers shouldn't be affected by battery saver,
+ * or battery saver is off.
+ */
+ public static final int LOCATION_MODE_NO_CHANGE = 0;
+
+ /**
+ * In this mode, the GPS based location provider should be disabled when battery saver is on and
+ * the device is non-interactive.
+ */
+ public static final int LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF = 1;
+
+ /**
+ * All location providers should be disabled when battery saver is on and
+ * the device is non-interactive.
+ */
+ public static final int LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF = 2;
+
+ /**
+ * In this mode, all the location providers will be kept available, but location fixes
+ * should only be provided to foreground apps.
+ */
+ public static final int LOCATION_MODE_FOREGROUND_ONLY = 3;
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"LOCATION_MODE_"}, value = {
+ LOCATION_MODE_NO_CHANGE,
+ LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF,
+ LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF,
+ LOCATION_MODE_FOREGROUND_ONLY,
+ })
+ public @interface LocationPowerSaveMode {}
+
final Context mContext;
final IPowerManager mService;
final Handler mHandler;
@@ -1143,6 +1179,24 @@
}
/**
+ * Returns how location features should behave when battery saver is on. When battery saver
+ * is off, this will always return {@link #LOCATION_MODE_NO_CHANGE}.
+ *
+ * <p>This API is normally only useful for components that provide location features.
+ *
+ * @see #isPowerSaveMode()
+ * @see #ACTION_POWER_SAVE_MODE_CHANGED
+ */
+ @LocationPowerSaveMode
+ public int getLocationPowerSaveMode() {
+ final PowerSaveState powerSaveState = getPowerSaveState(ServiceType.GPS);
+ if (!powerSaveState.globalBatterySaverEnabled) {
+ return LOCATION_MODE_NO_CHANGE;
+ }
+ return powerSaveState.gpsMode;
+ }
+
+ /**
* Returns true if the device is currently in idle mode. This happens when a device
* has been sitting unused and unmoving for a sufficiently long period of time, so that
* it decides to go into a lower power-use state. This may involve things like turning
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 0874d93..e6cf5e6 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -269,6 +269,15 @@
public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8;
/**
+ * Standard priority of video threads. Applications can not normally
+ * change to this priority.
+ * Use with {@link #setThreadPriority(int)} and
+ * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
+ * {@link java.lang.Thread} class.
+ */
+ public static final int THREAD_PRIORITY_VIDEO = -10;
+
+ /**
* Standard priority of audio threads. Applications can not normally
* change to this priority.
* Use with {@link #setThreadPriority(int)} and
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 44238df..13b5b5c9 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -209,6 +209,49 @@
public static final String DISALLOW_AIRPLANE_MODE = "no_airplane_mode";
/**
+ * Specifies if a user is disallowed from configuring brightness. When device owner sets it,
+ * it'll only be applied on the target(system) user.
+ *
+ * <p>The default value is <code>false</code>.
+ *
+ * <p>This user restriction has no effect on managed profiles.
+ * <p>Key for user restrictions.
+ * <p>Type: Boolean
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_CONFIG_BRIGHTNESS = "no_config_brightness";
+
+ /**
+ * Specifies if ambient display is disallowed for the user.
+ *
+ * <p>The default value is <code>false</code>.
+ *
+ * <p>This user restriction has no effect on managed profiles.
+ * <p>Key for user restrictions.
+ * <p>Type: Boolean
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_AMBIENT_DISPLAY = "no_ambient_display";
+
+ /**
+ * Specifies if a user is disallowed from changing screen off timeout.
+ *
+ * <p>The default value is <code>false</code>.
+ *
+ * <p>This user restriction has no effect on managed profiles.
+ * <p>Key for user restrictions.
+ * <p>Type: Boolean
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_CONFIG_SCREEN_TIMEOUT = "no_config_screen_timeout";
+
+ /**
* Specifies if a user is disallowed from enabling the
* "Unknown Sources" setting, that allows installation of apps from unknown sources.
* The default value is <code>false</code>.
@@ -748,6 +791,7 @@
* @see #getUserRestrictions()
* @hide
*/
+ @SystemApi
public static final String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background";
/**
@@ -877,6 +921,27 @@
public static final String DISALLOW_USER_SWITCH = "no_user_switch";
/**
+ * Specifies whether the user can share file / picture / data from the primary user into the
+ * managed profile, either by sending them from the primary side, or by picking up data within
+ * an app in the managed profile.
+ * <p>
+ * When a managed profile is created, the system allows the user to send data from the primary
+ * side to the profile by setting up certain default cross profile intent filters. If
+ * this is undesired, this restriction can be set to disallow it. Note that this restriction
+ * will not block any sharing allowed by explicit
+ * {@link DevicePolicyManager#addCrossProfileIntentFilter} calls by the profile owner.
+ * <p>
+ * This restriction is only meaningful when set by profile owner. When it is set by device
+ * owner, it does not have any effect.
+ * <p>
+ * The default value is <code>false</code>.
+ *
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_SHARE_INTO_MANAGED_PROFILE = "no_sharing_into_profile";
+ /**
* Application restriction key that is used to indicate the pending arrival
* of real restrictions for the app.
*
@@ -2194,6 +2259,12 @@
}
}
+ /** @removed */
+ @Deprecated
+ public boolean trySetQuietModeEnabled(boolean enableQuietMode, @NonNull UserHandle userHandle) {
+ return requestQuietModeEnabled(enableQuietMode, userHandle);
+ }
+
/**
* Enables or disables quiet mode for a managed profile. If quiet mode is enabled, apps in a
* managed profile don't run, generate notifications, or consume data or battery.
@@ -2219,22 +2290,22 @@
*
* @see #isQuietModeEnabled(UserHandle)
*/
- public boolean trySetQuietModeEnabled(boolean enableQuietMode, @NonNull UserHandle userHandle) {
- return trySetQuietModeEnabled(enableQuietMode, userHandle, null);
+ public boolean requestQuietModeEnabled(boolean enableQuietMode, @NonNull UserHandle userHandle) {
+ return requestQuietModeEnabled(enableQuietMode, userHandle, null);
}
/**
- * Similar to {@link #trySetQuietModeEnabled(boolean, UserHandle)}, except you can specify
+ * Similar to {@link #requestQuietModeEnabled(boolean, UserHandle)}, except you can specify
* a target to start when user is unlocked. If {@code target} is specified, caller must have
* the {@link android.Manifest.permission#MANAGE_USERS} permission.
*
- * @see {@link #trySetQuietModeEnabled(boolean, UserHandle)}
+ * @see {@link #requestQuietModeEnabled(boolean, UserHandle)}
* @hide
*/
- public boolean trySetQuietModeEnabled(
+ public boolean requestQuietModeEnabled(
boolean enableQuietMode, @NonNull UserHandle userHandle, IntentSender target) {
try {
- return mService.trySetQuietModeEnabled(
+ return mService.requestQuietModeEnabled(
mContext.getPackageName(), enableQuietMode, userHandle.getIdentifier(), target);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java
index 340f3fb..12a495b 100644
--- a/core/java/android/os/VintfObject.java
+++ b/core/java/android/os/VintfObject.java
@@ -76,8 +76,8 @@
/**
* @return a list of VNDK snapshots supported by the framework, as
* specified in framework manifest. For example,
- * [("25.0.5", ["libjpeg.so", "libbase.so"]),
- * ("25.1.3", ["libjpeg.so", "libbase.so"])]
+ * [("27", ["libjpeg.so", "libbase.so"]),
+ * ("28", ["libjpeg.so", "libbase.so"])]
*/
public static native Map<String, String[]> getVndkSnapshots();
}
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 070b8c1..839a8bf 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -394,4 +394,32 @@
parcel.writeString(mFsUuid);
parcel.writeString(mState);
}
+
+ /** {@hide} */
+ public static final class ScopedAccessProviderContract {
+
+ private ScopedAccessProviderContract() {
+ throw new UnsupportedOperationException("contains constants only");
+ }
+
+ public static final String AUTHORITY = "com.android.documentsui.scopedAccess";
+
+ public static final String TABLE_PACKAGES = "packages";
+ public static final String TABLE_PERMISSIONS = "permissions";
+
+ public static final String COL_PACKAGE = "package_name";
+ public static final String COL_VOLUME_UUID = "volume_uuid";
+ public static final String COL_DIRECTORY = "directory";
+ public static final String COL_GRANTED = "granted";
+
+ public static final String[] TABLE_PACKAGES_COLUMNS = new String[] { COL_PACKAGE };
+ public static final String[] TABLE_PERMISSIONS_COLUMNS =
+ new String[] { COL_PACKAGE, COL_VOLUME_UUID, COL_DIRECTORY, COL_GRANTED };
+
+ public static final int TABLE_PACKAGES_COL_PACKAGE = 0;
+ public static final int TABLE_PERMISSIONS_COL_PACKAGE = 0;
+ public static final int TABLE_PERMISSIONS_COL_VOLUME_UUID = 1;
+ public static final int TABLE_PERMISSIONS_COL_DIRECTORY = 2;
+ public static final int TABLE_PERMISSIONS_COL_GRANTED = 3;
+ }
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2ec4906..24e56c0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6802,6 +6802,37 @@
public static final String ASSIST_DISCLOSURE_ENABLED = "assist_disclosure_enabled";
/**
+ * Control if rotation suggestions are sent to System UI when in rotation locked mode.
+ * Done to enable screen rotation while the the screen rotation is locked. Enabling will
+ * poll the accelerometer in rotation locked mode.
+ *
+ * If 0, then rotation suggestions are not sent to System UI. If 1, suggestions are sent.
+ *
+ * @hide
+ */
+
+ public static final String SHOW_ROTATION_SUGGESTIONS = "show_rotation_suggestions";
+
+ /**
+ * The disabled state of SHOW_ROTATION_SUGGESTIONS.
+ * @hide
+ */
+ public static final int SHOW_ROTATION_SUGGESTIONS_DISABLED = 0x0;
+
+ /**
+ * The enabled state of SHOW_ROTATION_SUGGESTIONS.
+ * @hide
+ */
+ public static final int SHOW_ROTATION_SUGGESTIONS_ENABLED = 0x1;
+
+ /**
+ * The default state of SHOW_ROTATION_SUGGESTIONS.
+ * @hide
+ */
+ public static final int SHOW_ROTATION_SUGGESTIONS_DEFAULT =
+ SHOW_ROTATION_SUGGESTIONS_ENABLED;
+
+ /**
* Read only list of the service components that the current user has explicitly allowed to
* see and assist with all of the user's notifications.
*
@@ -9622,6 +9653,17 @@
public static final String ALWAYS_ON_DISPLAY_CONSTANTS = "always_on_display_constants";
/**
+ * System VDSO global setting. This links to the "sys.vdso" system property.
+ * The following values are supported:
+ * false -> both 32 and 64 bit vdso disabled
+ * 32 -> 32 bit vdso enabled
+ * 64 -> 64 bit vdso enabled
+ * Any other value defaults to both 32 bit and 64 bit true.
+ * @hide
+ */
+ public static final String SYS_VDSO = "sys_vdso";
+
+ /**
* App standby (app idle) specific settings.
* This is encoded as a key=value list, separated by commas. Ex:
* <p>
@@ -9802,6 +9844,23 @@
public static final java.lang.String APP_STANDBY_ENABLED = "app_standby_enabled";
/**
+ * Feature flag to enable or disable the Forced App Standby feature.
+ * Type: int (0 for false, 1 for true)
+ * Default: 1
+ * @hide
+ */
+ public static final String FORCED_APP_STANDBY_ENABLED = "forced_app_standby_enabled";
+
+ /**
+ * Whether or not to enable Forced App Standby on small battery devices.
+ * Type: int (0 for false, 1 for true)
+ * Default: 0
+ * @hide
+ */
+ public static final String FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED
+ = "forced_app_standby_for_small_battery_enabled";
+
+ /**
* Whether or not Network Watchlist feature is enabled.
* Type: int (0 for false, 1 for true)
* Default: 0
@@ -10019,6 +10078,7 @@
* If 1 low power mode is enabled.
* @hide
*/
+ @TestApi
public static final String LOW_POWER_MODE = "low_power";
/**
@@ -11240,6 +11300,22 @@
*/
public static final String ENABLE_GNSS_RAW_MEAS_FULL_TRACKING =
"enable_gnss_raw_meas_full_tracking";
+
+ /**
+ * Whether we've enabled zram on this device. Takes effect on
+ * reboot. The value "1" enables zram; "0" disables it, and
+ * everything else is unspecified.
+ * @hide
+ */
+ public static final String ZRAM_ENABLED =
+ "zram_enabled";
+
+ /**
+ * Whether smart replies in notifications are enabled.
+ * @hide
+ */
+ public static final String ENABLE_SMART_REPLIES_IN_NOTIFICATIONS =
+ "enable_smart_replies_in_notifications";
}
/**
diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java
index 6a3c55e..c568b6f 100644
--- a/core/java/android/provider/VoicemailContract.java
+++ b/core/java/android/provider/VoicemailContract.java
@@ -106,9 +106,12 @@
/**
* Broadcast intent to inform a new visual voicemail SMS has been received. This intent will
- * only be delivered to the telephony service. {@link #EXTRA_VOICEMAIL_SMS} will be included.
- */
- /** @hide */
+ * only be delivered to the telephony service.
+ *
+ * @see #EXTRA_VOICEMAIL_SMS
+ * @see #EXTRA_TARGET_PACKAGE
+ *
+ * @hide */
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_VOICEMAIL_SMS_RECEIVED =
"com.android.internal.provider.action.VOICEMAIL_SMS_RECEIVED";
@@ -121,6 +124,19 @@
public static final String EXTRA_VOICEMAIL_SMS = "android.provider.extra.VOICEMAIL_SMS";
/**
+ * Extra in {@link #ACTION_VOICEMAIL_SMS_RECEIVED} indicating the target package to bind {@link
+ * android.telephony.VisualVoicemailService}.
+ *
+ * <p>This extra should be set to android.telephony.VisualVoicemailSmsFilterSettings#packageName
+ * while performing filtering. Since the default dialer might change between the filter sending
+ * it and telephony binding to the service, this ensures the service will not receive SMS
+ * filtered by the previous app.
+ *
+ * @hide
+ */
+ public static final String EXTRA_TARGET_PACKAGE = "android.provider.extra.TARGET_PACAKGE";
+
+ /**
* Extra included in {@link Intent#ACTION_PROVIDER_CHANGED} broadcast intents to indicate if the
* receiving package made this change.
*/
@@ -172,6 +188,11 @@
*/
public static final String DURATION = Calls.DURATION;
/**
+ * Whether or not the voicemail has been acknowledged (notification sent to the user).
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String NEW = Calls.NEW;
+ /**
* Whether this item has been read or otherwise consumed by the user.
* <P>Type: INTEGER (boolean)</P>
*/
diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl
index 57477f5..b5496e4 100644
--- a/core/java/android/security/IKeystoreService.aidl
+++ b/core/java/android/security/IKeystoreService.aidl
@@ -51,7 +51,6 @@
String grant(String name, int granteeUid);
int ungrant(String name, int granteeUid);
long getmtime(String name, int uid);
- int duplicate(String srcKey, int srcUid, String destKey, int destUid);
int is_hardware_backed(String string);
int clear_uid(long uid);
diff --git a/core/java/android/os/Seccomp.java b/core/java/android/security/keystore/BadCertificateFormatException.java
similarity index 63%
copy from core/java/android/os/Seccomp.java
copy to core/java/android/security/keystore/BadCertificateFormatException.java
index f14e93f..ddc7bd2 100644
--- a/core/java/android/os/Seccomp.java
+++ b/core/java/android/security/keystore/BadCertificateFormatException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,11 +14,15 @@
* limitations under the License.
*/
-package android.os;
+package android.security.keystore;
/**
+ * Error thrown when the recovery agent supplies an invalid X509 certificate.
+ *
* @hide
*/
-public final class Seccomp {
- public static final native void setPolicy();
+public class BadCertificateFormatException extends RecoveryControllerException {
+ public BadCertificateFormatException(String msg) {
+ super(msg);
+ }
}
diff --git a/core/java/android/security/keystore/DecryptionFailedException.java b/core/java/android/security/keystore/DecryptionFailedException.java
new file mode 100644
index 0000000..945fcf6
--- /dev/null
+++ b/core/java/android/security/keystore/DecryptionFailedException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+/**
+ * Error thrown when decryption failed, due to an agent error. i.e., using the incorrect key,
+ * trying to decrypt garbage data, trying to decrypt data that has somehow been corrupted, etc.
+ *
+ * @hide
+ */
+public class DecryptionFailedException extends RecoveryControllerException {
+
+ public DecryptionFailedException(String msg) {
+ super(msg);
+ }
+}
diff --git a/core/java/android/security/keystore/InternalRecoveryServiceException.java b/core/java/android/security/keystore/InternalRecoveryServiceException.java
new file mode 100644
index 0000000..85829be
--- /dev/null
+++ b/core/java/android/security/keystore/InternalRecoveryServiceException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+/**
+ * An error thrown when something went wrong internally in the recovery service.
+ *
+ * <p>This is an unexpected error, and indicates a problem with the service itself, rather than the
+ * caller having performed some kind of illegal action.
+ *
+ * @hide
+ */
+public class InternalRecoveryServiceException extends RecoveryControllerException {
+ public InternalRecoveryServiceException(String msg) {
+ super(msg);
+ }
+
+ public InternalRecoveryServiceException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl b/core/java/android/security/keystore/KeyDerivationParams.aidl
similarity index 88%
copy from core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl
copy to core/java/android/security/keystore/KeyDerivationParams.aidl
index bd76051..f39aa04 100644
--- a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl
+++ b/core/java/android/security/keystore/KeyDerivationParams.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.security.recoverablekeystore;
+package android.security.keystore;
/* @hide */
-parcelable KeyStoreRecoveryData;
+parcelable KeyDerivationParams;
diff --git a/core/java/android/security/recoverablekeystore/KeyDerivationParameters.java b/core/java/android/security/keystore/KeyDerivationParams.java
similarity index 68%
rename from core/java/android/security/recoverablekeystore/KeyDerivationParameters.java
rename to core/java/android/security/keystore/KeyDerivationParams.java
index d162455..b702acc 100644
--- a/core/java/android/security/recoverablekeystore/KeyDerivationParameters.java
+++ b/core/java/android/security/keystore/KeyDerivationParams.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.security.recoverablekeystore;
+package android.security.keystore;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -28,21 +28,17 @@
/**
* Collection of parameters which define a key derivation function.
- * Supports
+ * Currently only supports salted SHA-256
*
- * <ul>
- * <li>SHA256
- * <li>Argon2id
- * </ul>
* @hide
*/
-public final class KeyDerivationParameters implements Parcelable {
+public final class KeyDerivationParams implements Parcelable {
private final int mAlgorithm;
private byte[] mSalt;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({ALGORITHM_SHA256, ALGORITHM_ARGON2ID})
+ @IntDef(prefix = {"ALGORITHM_"}, value = {ALGORITHM_SHA256, ALGORITHM_ARGON2ID})
public @interface KeyDerivationAlgorithm {
}
@@ -53,6 +49,7 @@
/**
* Argon2ID
+ * @hide
*/
// TODO: add Argon2ID support.
public static final int ALGORITHM_ARGON2ID = 2;
@@ -60,11 +57,11 @@
/**
* Creates instance of the class to to derive key using salted SHA256 hash.
*/
- public static KeyDerivationParameters createSha256Parameters(@NonNull byte[] salt) {
- return new KeyDerivationParameters(ALGORITHM_SHA256, salt);
+ public static KeyDerivationParams createSha256Params(@NonNull byte[] salt) {
+ return new KeyDerivationParams(ALGORITHM_SHA256, salt);
}
- private KeyDerivationParameters(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt) {
+ private KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt) {
mAlgorithm = algorithm;
mSalt = Preconditions.checkNotNull(salt);
}
@@ -83,24 +80,30 @@
return mSalt;
}
- public static final Parcelable.Creator<KeyDerivationParameters> CREATOR =
- new Parcelable.Creator<KeyDerivationParameters>() {
- public KeyDerivationParameters createFromParcel(Parcel in) {
- return new KeyDerivationParameters(in);
+ public static final Parcelable.Creator<KeyDerivationParams> CREATOR =
+ new Parcelable.Creator<KeyDerivationParams>() {
+ public KeyDerivationParams createFromParcel(Parcel in) {
+ return new KeyDerivationParams(in);
}
- public KeyDerivationParameters[] newArray(int length) {
- return new KeyDerivationParameters[length];
+ public KeyDerivationParams[] newArray(int length) {
+ return new KeyDerivationParams[length];
}
};
+ /**
+ * @hide
+ */
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mAlgorithm);
out.writeByteArray(mSalt);
}
- protected KeyDerivationParameters(Parcel in) {
+ /**
+ * @hide
+ */
+ protected KeyDerivationParams(Parcel in) {
mAlgorithm = in.readInt();
mSalt = in.createByteArray();
}
diff --git a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl b/core/java/android/security/keystore/KeychainProtectionParams.aidl
similarity index 88%
rename from core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl
rename to core/java/android/security/keystore/KeychainProtectionParams.aidl
index bd76051..0341223 100644
--- a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl
+++ b/core/java/android/security/keystore/KeychainProtectionParams.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.security.recoverablekeystore;
+package android.security.keystore;
/* @hide */
-parcelable KeyStoreRecoveryData;
+parcelable KeychainProtectionParams;
diff --git a/core/java/android/security/keystore/KeychainProtectionParams.java b/core/java/android/security/keystore/KeychainProtectionParams.java
new file mode 100644
index 0000000..a3cd431
--- /dev/null
+++ b/core/java/android/security/keystore/KeychainProtectionParams.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2017 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.security.keystore;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+
+/**
+ * A {@link KeychainSnapshot} is protected with a key derived from the user's lock screen. This
+ * class wraps all the data necessary to derive the same key on a recovering device:
+ *
+ * <ul>
+ * <li>UI parameters for the user's lock screen - so that if e.g., the user was using a pattern,
+ * the recovering device can display the pattern UI to the user when asking them to enter
+ * the lock screen from their previous device.
+ * <li>The algorithm used to derive a key from the user's lock screen, e.g. SHA-256 with a salt.
+ * </ul>
+ *
+ * <p>As such, this data is sent along with the {@link KeychainSnapshot} when syncing the current
+ * version of the keychain.
+ *
+ * <p>For now, the recoverable keychain only supports a single layer of protection, which is the
+ * user's lock screen. In the future, the keychain will support multiple layers of protection
+ * (e.g. an additional keychain password, along with the lock screen).
+ *
+ * @hide
+ */
+public final class KeychainProtectionParams implements Parcelable {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({TYPE_LOCKSCREEN, TYPE_CUSTOM_PASSWORD})
+ public @interface UserSecretType {
+ }
+
+ /**
+ * Lockscreen secret is required to recover KeyStore.
+ */
+ public static final int TYPE_LOCKSCREEN = 100;
+
+ /**
+ * Custom passphrase, unrelated to lock screen, is required to recover KeyStore.
+ */
+ public static final int TYPE_CUSTOM_PASSWORD = 101;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({TYPE_PIN, TYPE_PASSWORD, TYPE_PATTERN})
+ public @interface LockScreenUiFormat {
+ }
+
+ /**
+ * Pin with digits only.
+ */
+ public static final int TYPE_PIN = 1;
+
+ /**
+ * Password. String with latin-1 characters only.
+ */
+ public static final int TYPE_PASSWORD = 2;
+
+ /**
+ * Pattern with 3 by 3 grid.
+ */
+ public static final int TYPE_PATTERN = 3;
+
+ @UserSecretType
+ private Integer mUserSecretType;
+
+ @LockScreenUiFormat
+ private Integer mLockScreenUiFormat;
+
+ /**
+ * Parameters of the key derivation function, including algorithm, difficulty, salt.
+ */
+ private KeyDerivationParams mKeyDerivationParams;
+ private byte[] mSecret; // Derived from user secret. The field must have limited visibility.
+
+ /**
+ * @param secret Constructor creates a reference to the secret. Caller must use
+ * @link {#clearSecret} to overwrite its value in memory.
+ * @hide
+ */
+ public KeychainProtectionParams(@UserSecretType int userSecretType,
+ @LockScreenUiFormat int lockScreenUiFormat,
+ @NonNull KeyDerivationParams keyDerivationParams,
+ @NonNull byte[] secret) {
+ mUserSecretType = userSecretType;
+ mLockScreenUiFormat = lockScreenUiFormat;
+ mKeyDerivationParams = Preconditions.checkNotNull(keyDerivationParams);
+ mSecret = Preconditions.checkNotNull(secret);
+ }
+
+ private KeychainProtectionParams() {
+
+ }
+
+ /**
+ * @see TYPE_LOCKSCREEN
+ * @see TYPE_CUSTOM_PASSWORD
+ */
+ public @UserSecretType int getUserSecretType() {
+ return mUserSecretType;
+ }
+
+ /**
+ * Specifies UX shown to user during recovery.
+ * Default value is {@code TYPE_LOCKSCREEN}
+ *
+ * @see TYPE_PIN
+ * @see TYPE_PASSWORD
+ * @see TYPE_PATTERN
+ */
+ public @LockScreenUiFormat int getLockScreenUiFormat() {
+ return mLockScreenUiFormat;
+ }
+
+ /**
+ * Specifies function used to derive symmetric key from user input
+ * Format is defined in separate util class.
+ */
+ public @NonNull KeyDerivationParams getKeyDerivationParams() {
+ return mKeyDerivationParams;
+ }
+
+ /**
+ * Secret derived from user input.
+ * Default value is empty array
+ *
+ * @return secret or empty array
+ */
+ public @NonNull byte[] getSecret() {
+ return mSecret;
+ }
+
+ /**
+ * Builder for creating {@link KeychainProtectionParams}.
+ */
+ public static class Builder {
+ private KeychainProtectionParams mInstance = new KeychainProtectionParams();
+
+ /**
+ * Sets user secret type.
+ *
+ * @see TYPE_LOCKSCREEN
+ * @see TYPE_CUSTOM_PASSWORD
+ * @param userSecretType The secret type
+ * @return This builder.
+ */
+ public Builder setUserSecretType(@UserSecretType int userSecretType) {
+ mInstance.mUserSecretType = userSecretType;
+ return this;
+ }
+
+ /**
+ * Sets UI format.
+ *
+ * @see TYPE_PIN
+ * @see TYPE_PASSWORD
+ * @see TYPE_PATTERN
+ * @param lockScreenUiFormat The UI format
+ * @return This builder.
+ */
+ public Builder setLockScreenUiFormat(@LockScreenUiFormat int lockScreenUiFormat) {
+ mInstance.mLockScreenUiFormat = lockScreenUiFormat;
+ return this;
+ }
+
+ /**
+ * Sets parameters of the key derivation function.
+ *
+ * @param keyDerivationParams Key derivation Params
+ * @return This builder.
+ */
+ public Builder setKeyDerivationParams(@NonNull KeyDerivationParams
+ keyDerivationParams) {
+ mInstance.mKeyDerivationParams = keyDerivationParams;
+ return this;
+ }
+
+ /**
+ * Secret derived from user input, or empty array.
+ *
+ * @param secret The secret.
+ * @return This builder.
+ */
+ public Builder setSecret(@NonNull byte[] secret) {
+ mInstance.mSecret = secret;
+ return this;
+ }
+
+
+ /**
+ * Creates a new {@link KeychainProtectionParams} instance.
+ * The instance will include default values, if {@link setSecret}
+ * or {@link setUserSecretType} were not called.
+ *
+ * @return new instance
+ * @throws NullPointerException if some required fields were not set.
+ */
+ @NonNull public KeychainProtectionParams build() {
+ if (mInstance.mUserSecretType == null) {
+ mInstance.mUserSecretType = TYPE_LOCKSCREEN;
+ }
+ Preconditions.checkNotNull(mInstance.mLockScreenUiFormat);
+ Preconditions.checkNotNull(mInstance.mKeyDerivationParams);
+ if (mInstance.mSecret == null) {
+ mInstance.mSecret = new byte[]{};
+ }
+ return mInstance;
+ }
+ }
+
+ /**
+ * Removes secret from memory than object is no longer used.
+ * Since finalizer call is not reliable, please use @link {#clearSecret} directly.
+ */
+ @Override
+ protected void finalize() throws Throwable {
+ clearSecret();
+ super.finalize();
+ }
+
+ /**
+ * Fills mSecret with zeroes.
+ */
+ public void clearSecret() {
+ Arrays.fill(mSecret, (byte) 0);
+ }
+
+ public static final Parcelable.Creator<KeychainProtectionParams> CREATOR =
+ new Parcelable.Creator<KeychainProtectionParams>() {
+ public KeychainProtectionParams createFromParcel(Parcel in) {
+ return new KeychainProtectionParams(in);
+ }
+
+ public KeychainProtectionParams[] newArray(int length) {
+ return new KeychainProtectionParams[length];
+ }
+ };
+
+ /**
+ * @hide
+ */
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mUserSecretType);
+ out.writeInt(mLockScreenUiFormat);
+ out.writeTypedObject(mKeyDerivationParams, flags);
+ out.writeByteArray(mSecret);
+ }
+
+ /**
+ * @hide
+ */
+ protected KeychainProtectionParams(Parcel in) {
+ mUserSecretType = in.readInt();
+ mLockScreenUiFormat = in.readInt();
+ mKeyDerivationParams = in.readTypedObject(KeyDerivationParams.CREATOR);
+ mSecret = in.createByteArray();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl b/core/java/android/security/keystore/KeychainSnapshot.aidl
similarity index 88%
copy from core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl
copy to core/java/android/security/keystore/KeychainSnapshot.aidl
index bd76051..b35713f 100644
--- a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl
+++ b/core/java/android/security/keystore/KeychainSnapshot.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.security.recoverablekeystore;
+package android.security.keystore;
/* @hide */
-parcelable KeyStoreRecoveryData;
+parcelable KeychainSnapshot;
diff --git a/core/java/android/security/keystore/KeychainSnapshot.java b/core/java/android/security/keystore/KeychainSnapshot.java
new file mode 100644
index 0000000..2029114
--- /dev/null
+++ b/core/java/android/security/keystore/KeychainSnapshot.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2017 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.security.keystore;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+
+/**
+ * A snapshot of a version of the keystore. Two events can trigger the generation of a new snapshot:
+ *
+ * <ul>
+ * <li>The user's lock screen changes. (A key derived from the user's lock screen is used to
+ * protected the keychain, which is why this forces a new snapshot.)
+ * <li>A key is added to or removed from the recoverable keychain.
+ * </ul>
+ *
+ * <p>The snapshot data is also encrypted with the remote trusted hardware's public key, so even
+ * the recovery agent itself should not be able to decipher the data. The recovery agent sends an
+ * instance of this to the remote trusted hardware whenever a new snapshot is generated. During a
+ * recovery flow, the recovery agent retrieves a snapshot from the remote trusted hardware. It then
+ * sends it to the framework, where it is decrypted using the user's lock screen from their previous
+ * device.
+ *
+ * @hide
+ */
+public final class KeychainSnapshot implements Parcelable {
+ private int mSnapshotVersion;
+ private List<KeychainProtectionParams> mKeychainProtectionParams;
+ private List<WrappedApplicationKey> mEntryRecoveryData;
+ private byte[] mEncryptedRecoveryKeyBlob;
+
+ /**
+ * @hide
+ * Deprecated, consider using builder.
+ */
+ public KeychainSnapshot(
+ int snapshotVersion,
+ @NonNull List<KeychainProtectionParams> keychainProtectionParams,
+ @NonNull List<WrappedApplicationKey> wrappedApplicationKeys,
+ @NonNull byte[] encryptedRecoveryKeyBlob) {
+ mSnapshotVersion = snapshotVersion;
+ mKeychainProtectionParams =
+ Preconditions.checkCollectionElementsNotNull(keychainProtectionParams,
+ "keychainProtectionParams");
+ mEntryRecoveryData = Preconditions.checkCollectionElementsNotNull(wrappedApplicationKeys,
+ "wrappedApplicationKeys");
+ mEncryptedRecoveryKeyBlob = Preconditions.checkNotNull(encryptedRecoveryKeyBlob);
+ }
+
+ private KeychainSnapshot() {
+
+ }
+
+ /**
+ * Snapshot version for given account. It is incremented when user secret or list of application
+ * keys changes.
+ */
+ public int getSnapshotVersion() {
+ return mSnapshotVersion;
+ }
+
+ /**
+ * UI and key derivation parameters. Note that combination of secrets may be used.
+ */
+ public @NonNull List<KeychainProtectionParams> getKeychainProtectionParams() {
+ return mKeychainProtectionParams;
+ }
+
+ /**
+ * List of application keys, with key material encrypted by
+ * the recovery key ({@link #getEncryptedRecoveryKeyBlob}).
+ */
+ public @NonNull List<WrappedApplicationKey> getWrappedApplicationKeys() {
+ return mEntryRecoveryData;
+ }
+
+ /**
+ * Recovery key blob, encrypted by user secret and recovery service public key.
+ */
+ public @NonNull byte[] getEncryptedRecoveryKeyBlob() {
+ return mEncryptedRecoveryKeyBlob;
+ }
+
+ public static final Parcelable.Creator<KeychainSnapshot> CREATOR =
+ new Parcelable.Creator<KeychainSnapshot>() {
+ public KeychainSnapshot createFromParcel(Parcel in) {
+ return new KeychainSnapshot(in);
+ }
+
+ public KeychainSnapshot[] newArray(int length) {
+ return new KeychainSnapshot[length];
+ }
+ };
+
+ /**
+ * Builder for creating {@link KeychainSnapshot}.
+ */
+ public static class Builder {
+ private KeychainSnapshot mInstance = new KeychainSnapshot();
+
+ /**
+ * Snapshot version for given account.
+ *
+ * @param snapshotVersion The snapshot version
+ * @return This builder.
+ */
+ public Builder setSnapshotVersion(int snapshotVersion) {
+ mInstance.mSnapshotVersion = snapshotVersion;
+ return this;
+ }
+
+ /**
+ * Sets UI and key derivation parameters
+ *
+ * @param recoveryMetadata The UI and key derivation parameters
+ * @return This builder.
+ */
+ public Builder setKeychainProtectionParams(
+ @NonNull List<KeychainProtectionParams> recoveryMetadata) {
+ mInstance.mKeychainProtectionParams = recoveryMetadata;
+ return this;
+ }
+
+ /**
+ * List of application keys.
+ *
+ * @param entryRecoveryData List of application keys
+ * @return This builder.
+ */
+ public Builder setWrappedApplicationKeys(List<WrappedApplicationKey> entryRecoveryData) {
+ mInstance.mEntryRecoveryData = entryRecoveryData;
+ return this;
+ }
+
+ /**
+ * Sets recovery key blob
+ *
+ * @param encryptedRecoveryKeyBlob The recovery key blob.
+ * @return This builder.
+ */
+ public Builder setEncryptedRecoveryKeyBlob(@NonNull byte[] encryptedRecoveryKeyBlob) {
+ mInstance.mEncryptedRecoveryKeyBlob = encryptedRecoveryKeyBlob;
+ return this;
+ }
+
+
+ /**
+ * Creates a new {@link KeychainSnapshot} instance.
+ *
+ * @return new instance
+ * @throws NullPointerException if some required fields were not set.
+ */
+ @NonNull public KeychainSnapshot build() {
+ Preconditions.checkCollectionElementsNotNull(mInstance.mKeychainProtectionParams,
+ "recoveryMetadata");
+ Preconditions.checkCollectionElementsNotNull(mInstance.mEntryRecoveryData,
+ "entryRecoveryData");
+ Preconditions.checkNotNull(mInstance.mEncryptedRecoveryKeyBlob);
+ return mInstance;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mSnapshotVersion);
+ out.writeTypedList(mKeychainProtectionParams);
+ out.writeByteArray(mEncryptedRecoveryKeyBlob);
+ out.writeTypedList(mEntryRecoveryData);
+ }
+
+ /**
+ * @hide
+ */
+ protected KeychainSnapshot(Parcel in) {
+ mSnapshotVersion = in.readInt();
+ mKeychainProtectionParams = in.createTypedArrayList(KeychainProtectionParams.CREATOR);
+ mEncryptedRecoveryKeyBlob = in.createByteArray();
+ mEntryRecoveryData = in.createTypedArrayList(WrappedApplicationKey.CREATOR);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/security/keystore/LockScreenRequiredException.java b/core/java/android/security/keystore/LockScreenRequiredException.java
new file mode 100644
index 0000000..b07fb9c
--- /dev/null
+++ b/core/java/android/security/keystore/LockScreenRequiredException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+/**
+ * Error thrown when trying to generate keys for a profile that has no lock screen set.
+ *
+ * <p>A lock screen must be set, as the lock screen is used to encrypt the snapshot.
+ *
+ * @hide
+ */
+public class LockScreenRequiredException extends RecoveryControllerException {
+ public LockScreenRequiredException(String msg) {
+ super(msg);
+ }
+}
diff --git a/core/java/android/security/keystore/RecoveryControllerException.java b/core/java/android/security/keystore/RecoveryControllerException.java
new file mode 100644
index 0000000..31fd4af
--- /dev/null
+++ b/core/java/android/security/keystore/RecoveryControllerException.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+import java.security.GeneralSecurityException;
+
+/**
+ * Base exception for errors thrown by {@link RecoveryManager}.
+ *
+ * @hide
+ */
+public abstract class RecoveryControllerException extends GeneralSecurityException {
+ RecoveryControllerException() { }
+
+ RecoveryControllerException(String msg) {
+ super(msg);
+ }
+
+ public RecoveryControllerException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/core/java/android/security/keystore/RecoveryManager.java b/core/java/android/security/keystore/RecoveryManager.java
new file mode 100644
index 0000000..f01ed06
--- /dev/null
+++ b/core/java/android/security/keystore/RecoveryManager.java
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2017 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.security.keystore;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
+
+import com.android.internal.widget.ILockSettings;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A wrapper around KeyStore which lets key be exported to trusted hardware on server side and
+ * recovered later.
+ *
+ * @hide
+ */
+public class RecoveryManager {
+
+ /** Key has been successfully synced. */
+ public static final int RECOVERY_STATUS_SYNCED = 0;
+ /** Waiting for recovery agent to sync the key. */
+ public static final int RECOVERY_STATUS_SYNC_IN_PROGRESS = 1;
+ /** Recovery account is not available. */
+ public static final int RECOVERY_STATUS_MISSING_ACCOUNT = 2;
+ /** Key cannot be synced. */
+ public static final int RECOVERY_STATUS_PERMANENT_FAILURE = 3;
+
+ /**
+ * Failed because no snapshot is yet pending to be synced for the user.
+ *
+ * @hide
+ */
+ public static final int ERROR_NO_SNAPSHOT_PENDING = 21;
+
+ /**
+ * Failed due to an error internal to the recovery service. This is unexpected and indicates
+ * either a problem with the logic in the service, or a problem with a dependency of the
+ * service (such as AndroidKeyStore).
+ *
+ * @hide
+ */
+ public static final int ERROR_SERVICE_INTERNAL_ERROR = 22;
+
+ /**
+ * Failed because the user does not have a lock screen set.
+ *
+ * @hide
+ */
+ public static final int ERROR_INSECURE_USER = 23;
+
+ /**
+ * Error thrown when attempting to use a recovery session that has since been closed.
+ *
+ * @hide
+ */
+ public static final int ERROR_SESSION_EXPIRED = 24;
+
+ /**
+ * Failed because the provided certificate was not a valid X509 certificate.
+ *
+ * @hide
+ */
+ public static final int ERROR_BAD_CERTIFICATE_FORMAT = 25;
+
+ /**
+ * Error thrown if decryption failed. This might be because the tag is wrong, the key is wrong,
+ * the data has become corrupted, the data has been tampered with, etc.
+ *
+ * @hide
+ */
+ public static final int ERROR_DECRYPTION_FAILED = 26;
+
+
+ private final ILockSettings mBinder;
+
+ private RecoveryManager(ILockSettings binder) {
+ mBinder = binder;
+ }
+
+ /**
+ * Gets a new instance of the class.
+ */
+ public static RecoveryManager getInstance() {
+ ILockSettings lockSettings =
+ ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"));
+ return new RecoveryManager(lockSettings);
+ }
+
+ /**
+ * Initializes key recovery service for the calling application. RecoveryManager
+ * randomly chooses one of the keys from the list and keeps it to use for future key export
+ * operations. Collection of all keys in the list must be signed by the provided {@code
+ * rootCertificateAlias}, which must also be present in the list of root certificates
+ * preinstalled on the device. The random selection allows RecoveryManager to select
+ * which of a set of remote recovery service devices will be used.
+ *
+ * <p>In addition, RecoveryManager enforces a delay of three months between
+ * consecutive initialization attempts, to limit the ability of an attacker to often switch
+ * remote recovery devices and significantly increase number of recovery attempts.
+ *
+ * @param rootCertificateAlias alias of a root certificate preinstalled on the device
+ * @param signedPublicKeyList binary blob a list of X509 certificates and signature
+ * @throws BadCertificateFormatException if the {@code signedPublicKeyList} is in a bad format.
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ */
+ public void initRecoveryService(
+ @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList)
+ throws BadCertificateFormatException, InternalRecoveryServiceException {
+ try {
+ mBinder.initRecoveryService(rootCertificateAlias, signedPublicKeyList);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ERROR_BAD_CERTIFICATE_FORMAT) {
+ throw new BadCertificateFormatException(e.getMessage());
+ }
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Returns data necessary to store all recoverable keys for given account. Key material is
+ * encrypted with user secret and recovery public key.
+ *
+ * @param account specific to Recovery agent.
+ * @return Data necessary to recover keystore.
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ */
+ public @NonNull KeychainSnapshot getRecoveryData(@NonNull byte[] account)
+ throws InternalRecoveryServiceException {
+ try {
+ return mBinder.getRecoveryData(account);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ERROR_NO_SNAPSHOT_PENDING) {
+ return null;
+ }
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Sets a listener which notifies recovery agent that new recovery snapshot is available. {@link
+ * #getRecoveryData} can be used to get the snapshot. Note that every recovery agent can have at
+ * most one registered listener at any time.
+ *
+ * @param intent triggered when new snapshot is available. Unregisters listener if the value is
+ * {@code null}.
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ */
+ public void setSnapshotCreatedPendingIntent(@Nullable PendingIntent intent)
+ throws InternalRecoveryServiceException {
+ try {
+ mBinder.setSnapshotCreatedPendingIntent(intent);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Returns a map from recovery agent accounts to corresponding KeyStore recovery snapshot
+ * version. Version zero is used, if no snapshots were created for the account.
+ *
+ * @return Map from recovery agent accounts to snapshot versions.
+ * @see KeychainSnapshot#getSnapshotVersion
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ */
+ public @NonNull Map<byte[], Integer> getRecoverySnapshotVersions()
+ throws InternalRecoveryServiceException {
+ try {
+ // IPC doesn't support generic Maps.
+ @SuppressWarnings("unchecked")
+ Map<byte[], Integer> result =
+ (Map<byte[], Integer>) mBinder.getRecoverySnapshotVersions();
+ return result;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Server parameters used to generate new recovery key blobs. This value will be included in
+ * {@code KeychainSnapshot.getEncryptedRecoveryKeyBlob()}. The same value must be included
+ * in vaultParams {@link #startRecoverySession}
+ *
+ * @param serverParams included in recovery key blob.
+ * @see #getRecoveryData
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ */
+ public void setServerParams(byte[] serverParams) throws InternalRecoveryServiceException {
+ try {
+ mBinder.setServerParams(serverParams);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Updates recovery status for given keys. It is used to notify keystore that key was
+ * successfully stored on the server or there were an error. Application can check this value
+ * using {@code getRecoveyStatus}.
+ *
+ * @param packageName Application whose recoverable keys' statuses are to be updated.
+ * @param aliases List of application-specific key aliases. If the array is empty, updates the
+ * status for all existing recoverable keys.
+ * @param status Status specific to recovery agent.
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ */
+ public void setRecoveryStatus(
+ @NonNull String packageName, @Nullable String[] aliases, int status)
+ throws NameNotFoundException, InternalRecoveryServiceException {
+ try {
+ mBinder.setRecoveryStatus(packageName, aliases, status);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Returns a {@code Map} from Application's KeyStore key aliases to their recovery status.
+ * Negative status values are reserved for recovery agent specific codes. List of common codes:
+ *
+ * <ul>
+ * <li>{@link #RECOVERY_STATUS_SYNCED}
+ * <li>{@link #RECOVERY_STATUS_SYNC_IN_PROGRESS}
+ * <li>{@link #RECOVERY_STATUS_MISSING_ACCOUNT}
+ * <li>{@link #RECOVERY_STATUS_PERMANENT_FAILURE}
+ * </ul>
+ *
+ * @return {@code Map} from KeyStore alias to recovery status.
+ * @see #setRecoveryStatus
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ */
+ public Map<String, Integer> getRecoveryStatus() throws InternalRecoveryServiceException {
+ try {
+ // IPC doesn't support generic Maps.
+ @SuppressWarnings("unchecked")
+ Map<String, Integer> result =
+ (Map<String, Integer>) mBinder.getRecoveryStatus(/*packageName=*/ null);
+ return result;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Specifies a set of secret types used for end-to-end keystore encryption. Knowing all of them
+ * is necessary to recover data.
+ *
+ * @param secretTypes {@link KeychainProtectionParams#TYPE_LOCKSCREEN} or {@link
+ * KeychainProtectionParams#TYPE_CUSTOM_PASSWORD}
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ */
+ public void setRecoverySecretTypes(
+ @NonNull @KeychainProtectionParams.UserSecretType int[] secretTypes)
+ throws InternalRecoveryServiceException {
+ try {
+ mBinder.setRecoverySecretTypes(secretTypes);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Defines a set of secret types used for end-to-end keystore encryption. Knowing all of them is
+ * necessary to generate KeychainSnapshot.
+ *
+ * @return list of recovery secret types
+ * @see KeychainSnapshot
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ */
+ public @NonNull @KeychainProtectionParams.UserSecretType int[] getRecoverySecretTypes()
+ throws InternalRecoveryServiceException {
+ try {
+ return mBinder.getRecoverySecretTypes();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Returns a list of recovery secret types, necessary to create a pending recovery snapshot.
+ * When user enters a secret of a pending type {@link #recoverySecretAvailable} should be
+ * called.
+ *
+ * @return list of recovery secret types
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ */
+ @NonNull
+ public @KeychainProtectionParams.UserSecretType int[] getPendingRecoverySecretTypes()
+ throws InternalRecoveryServiceException {
+ try {
+ return mBinder.getPendingRecoverySecretTypes();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Method notifies KeyStore that a user-generated secret is available. This method generates a
+ * symmetric session key which a trusted remote device can use to return a recovery key. Caller
+ * should use {@link KeychainProtectionParams#clearSecret} to override the secret value in
+ * memory.
+ *
+ * @param recoverySecret user generated secret together with parameters necessary to regenerate
+ * it on a new device.
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ */
+ public void recoverySecretAvailable(@NonNull KeychainProtectionParams recoverySecret)
+ throws InternalRecoveryServiceException {
+ try {
+ mBinder.recoverySecretAvailable(recoverySecret);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Initializes recovery session and returns a blob with proof of recovery secrets possession.
+ * The method generates symmetric key for a session, which trusted remote device can use to
+ * return recovery key.
+ *
+ * @param sessionId ID for recovery session.
+ * @param verifierPublicKey Encoded {@code java.security.cert.X509Certificate} with Public key
+ * used to create the recovery blob on the source device.
+ * Keystore will verify the certificate using root of trust.
+ * @param vaultParams Must match the parameters in the corresponding field in the recovery blob.
+ * Used to limit number of guesses.
+ * @param vaultChallenge Data passed from server for this recovery session and used to prevent
+ * replay attacks
+ * @param secrets Secrets provided by user, the method only uses type and secret fields.
+ * @return Binary blob with recovery claim. It is encrypted with verifierPublicKey and contains
+ * a proof of user secrets, session symmetric key and parameters necessary to identify the
+ * counter with the number of failed recovery attempts.
+ * @throws BadCertificateFormatException if the {@code verifierPublicKey} is in an incorrect
+ * format.
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ */
+ public @NonNull byte[] startRecoverySession(
+ @NonNull String sessionId,
+ @NonNull byte[] verifierPublicKey,
+ @NonNull byte[] vaultParams,
+ @NonNull byte[] vaultChallenge,
+ @NonNull List<KeychainProtectionParams> secrets)
+ throws BadCertificateFormatException, InternalRecoveryServiceException {
+ try {
+ byte[] recoveryClaim =
+ mBinder.startRecoverySession(
+ sessionId,
+ verifierPublicKey,
+ vaultParams,
+ vaultChallenge,
+ secrets);
+ return recoveryClaim;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ERROR_BAD_CERTIFICATE_FORMAT) {
+ throw new BadCertificateFormatException(e.getMessage());
+ }
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Imports keys.
+ *
+ * @param sessionId Id for recovery session, same as in
+ * {@link #startRecoverySession(String, byte[], byte[], byte[], List)}.
+ * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
+ * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
+ * and session. KeyStore only uses package names from the application info in {@link
+ * WrappedApplicationKey}. Caller is responsibility to perform certificates check.
+ * @return Map from alias to raw key material.
+ * @throws SessionExpiredException if {@code session} has since been closed.
+ * @throws DecryptionFailedException if unable to decrypt the snapshot.
+ * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service.
+ */
+ public Map<String, byte[]> recoverKeys(
+ @NonNull String sessionId,
+ @NonNull byte[] recoveryKeyBlob,
+ @NonNull List<WrappedApplicationKey> applicationKeys)
+ throws SessionExpiredException, DecryptionFailedException,
+ InternalRecoveryServiceException {
+ try {
+ return (Map<String, byte[]>) mBinder.recoverKeys(
+ sessionId, recoveryKeyBlob, applicationKeys);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ERROR_DECRYPTION_FAILED) {
+ throw new DecryptionFailedException(e.getMessage());
+ }
+ if (e.errorCode == ERROR_SESSION_EXPIRED) {
+ throw new SessionExpiredException(e.getMessage());
+ }
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Generates a key called {@code alias} and loads it into the recoverable key store. Returns the
+ * raw material of the key.
+ *
+ * @param alias The key alias.
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ * @throws LockScreenRequiredException if the user has not set a lock screen. This is required
+ * to generate recoverable keys, as the snapshots are encrypted using a key derived from the
+ * lock screen.
+ */
+ public byte[] generateAndStoreKey(@NonNull String alias)
+ throws InternalRecoveryServiceException, LockScreenRequiredException {
+ try {
+ return mBinder.generateAndStoreKey(alias);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ERROR_INSECURE_USER) {
+ throw new LockScreenRequiredException(e.getMessage());
+ }
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Removes a key called {@code alias} from the recoverable key store.
+ *
+ * @param alias The key alias.
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ */
+ public void removeKey(@NonNull String alias) throws InternalRecoveryServiceException {
+ try {
+ mBinder.removeKey(alias);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ private InternalRecoveryServiceException wrapUnexpectedServiceSpecificException(
+ ServiceSpecificException e) {
+ if (e.errorCode == ERROR_SERVICE_INTERNAL_ERROR) {
+ return new InternalRecoveryServiceException(e.getMessage());
+ }
+
+ // Should never happen. If it does, it's a bug, and we need to update how the method that
+ // called this throws its exceptions.
+ return new InternalRecoveryServiceException("Unexpected error code for method: "
+ + e.errorCode, e);
+ }
+}
diff --git a/core/java/android/os/Seccomp.java b/core/java/android/security/keystore/SessionExpiredException.java
similarity index 63%
copy from core/java/android/os/Seccomp.java
copy to core/java/android/security/keystore/SessionExpiredException.java
index f14e93f..f13e206 100644
--- a/core/java/android/os/Seccomp.java
+++ b/core/java/android/security/keystore/SessionExpiredException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,11 +14,15 @@
* limitations under the License.
*/
-package android.os;
+package android.security.keystore;
/**
+ * Error thrown when attempting to use a {@link RecoverySession} that has since expired.
+ *
* @hide
*/
-public final class Seccomp {
- public static final native void setPolicy();
+public class SessionExpiredException extends RecoveryControllerException {
+ public SessionExpiredException(String msg) {
+ super(msg);
+ }
}
diff --git a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl b/core/java/android/security/keystore/WrappedApplicationKey.aidl
similarity index 88%
copy from core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl
copy to core/java/android/security/keystore/WrappedApplicationKey.aidl
index bd76051..a6294fe 100644
--- a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl
+++ b/core/java/android/security/keystore/WrappedApplicationKey.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.security.recoverablekeystore;
+package android.security.keystore;
/* @hide */
-parcelable KeyStoreRecoveryData;
+parcelable WrappedApplicationKey;
diff --git a/core/java/android/security/keystore/WrappedApplicationKey.java b/core/java/android/security/keystore/WrappedApplicationKey.java
new file mode 100644
index 0000000..522bb95
--- /dev/null
+++ b/core/java/android/security/keystore/WrappedApplicationKey.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2017 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.security.keystore;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Helper class with data necessary recover a single application key, given a recovery key.
+ *
+ * <ul>
+ * <li>Alias - Keystore alias of the key.
+ * <li>Encrypted key material.
+ * </ul>
+ *
+ * Note that Application info is not included. Recovery Agent can only make its own keys
+ * recoverable.
+ *
+ * @hide
+ */
+public final class WrappedApplicationKey implements Parcelable {
+ private String mAlias;
+ // The only supported format is AES-256 symmetric key.
+ private byte[] mEncryptedKeyMaterial;
+
+ /**
+ * Builder for creating {@link WrappedApplicationKey}.
+ */
+ public static class Builder {
+ private WrappedApplicationKey mInstance = new WrappedApplicationKey();
+
+ /**
+ * Sets Application-specific alias of the key.
+ *
+ * @param alias The alias.
+ * @return This builder.
+ */
+ public Builder setAlias(@NonNull String alias) {
+ mInstance.mAlias = alias;
+ return this;
+ }
+
+ /**
+ * Sets key material encrypted by recovery key.
+ *
+ * @param encryptedKeyMaterial The key material
+ * @return This builder
+ */
+
+ public Builder setEncryptedKeyMaterial(@NonNull byte[] encryptedKeyMaterial) {
+ mInstance.mEncryptedKeyMaterial = encryptedKeyMaterial;
+ return this;
+ }
+
+ /**
+ * Creates a new {@link WrappedApplicationKey} instance.
+ *
+ * @return new instance
+ * @throws NullPointerException if some required fields were not set.
+ */
+ @NonNull public WrappedApplicationKey build() {
+ Preconditions.checkNotNull(mInstance.mAlias);
+ Preconditions.checkNotNull(mInstance.mEncryptedKeyMaterial);
+ return mInstance;
+ }
+ }
+
+ private WrappedApplicationKey() {
+
+ }
+
+ /**
+ * Deprecated - consider using Builder.
+ * @hide
+ */
+ public WrappedApplicationKey(@NonNull String alias, @NonNull byte[] encryptedKeyMaterial) {
+ mAlias = Preconditions.checkNotNull(alias);
+ mEncryptedKeyMaterial = Preconditions.checkNotNull(encryptedKeyMaterial);
+ }
+
+ /**
+ * Application-specific alias of the key.
+ *
+ * @see java.security.KeyStore.aliases
+ */
+ public @NonNull String getAlias() {
+ return mAlias;
+ }
+
+ /** Key material encrypted by recovery key. */
+ public @NonNull byte[] getEncryptedKeyMaterial() {
+ return mEncryptedKeyMaterial;
+ }
+
+ public static final Parcelable.Creator<WrappedApplicationKey> CREATOR =
+ new Parcelable.Creator<WrappedApplicationKey>() {
+ public WrappedApplicationKey createFromParcel(Parcel in) {
+ return new WrappedApplicationKey(in);
+ }
+
+ public WrappedApplicationKey[] newArray(int length) {
+ return new WrappedApplicationKey[length];
+ }
+ };
+
+ /**
+ * @hide
+ */
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mAlias);
+ out.writeByteArray(mEncryptedKeyMaterial);
+ }
+
+ /**
+ * @hide
+ */
+ protected WrappedApplicationKey(Parcel in) {
+ mAlias = in.readString();
+ mEncryptedKeyMaterial = in.createByteArray();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.aidl b/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.aidl
deleted file mode 100644
index 1674e51..0000000
--- a/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2017 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.security.recoverablekeystore;
-
-/* @hide */
-parcelable KeyEntryRecoveryData;
diff --git a/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.java b/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.java
deleted file mode 100644
index 5f56c91..0000000
--- a/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2017 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.security.recoverablekeystore;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.util.Preconditions;
-
-/**
- * Helper class with data necessary recover a single application key, given a recovery key.
- *
- * <ul>
- * <li>Alias - Keystore alias of the key.
- * <li>Encrypted key material.
- * </ul>
- *
- * Note that Application info is not included. Recovery Agent can only make its own keys
- * recoverable.
- *
- * @hide
- */
-public final class KeyEntryRecoveryData implements Parcelable {
- private final String mAlias;
- // The only supported format is AES-256 symmetric key.
- private final byte[] mEncryptedKeyMaterial;
-
- public KeyEntryRecoveryData(@NonNull String alias, @NonNull byte[] encryptedKeyMaterial) {
- mAlias = Preconditions.checkNotNull(alias);
- mEncryptedKeyMaterial = Preconditions.checkNotNull(encryptedKeyMaterial);
- }
-
- /**
- * Application-specific alias of the key.
- *
- * @see java.security.KeyStore.aliases
- */
- public @NonNull String getAlias() {
- return mAlias;
- }
-
- /** Encrypted key material encrypted by recovery key. */
- public @NonNull byte[] getEncryptedKeyMaterial() {
- return mEncryptedKeyMaterial;
- }
-
- public static final Parcelable.Creator<KeyEntryRecoveryData> CREATOR =
- new Parcelable.Creator<KeyEntryRecoveryData>() {
- public KeyEntryRecoveryData createFromParcel(Parcel in) {
- return new KeyEntryRecoveryData(in);
- }
-
- public KeyEntryRecoveryData[] newArray(int length) {
- return new KeyEntryRecoveryData[length];
- }
- };
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(mAlias);
- out.writeByteArray(mEncryptedKeyMaterial);
- }
-
- protected KeyEntryRecoveryData(Parcel in) {
- mAlias = in.readString();
- mEncryptedKeyMaterial = in.createByteArray();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-}
diff --git a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.java b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.java
deleted file mode 100644
index 087f7a2..0000000
--- a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2017 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.security.recoverablekeystore;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.util.Preconditions;
-
-import java.util.List;
-
-/**
- * Helper class which returns data necessary to recover keys.
- * Contains
- *
- * <ul>
- * <li>Snapshot version.
- * <li>Recovery metadata with UI and key derivation parameters.
- * <li>List of application keys encrypted by recovery key.
- * <li>Encrypted recovery key.
- * </ul>
- *
- * @hide
- */
-public final class KeyStoreRecoveryData implements Parcelable {
- private final int mSnapshotVersion;
- private final List<KeyStoreRecoveryMetadata> mRecoveryMetadata;
- private final List<KeyEntryRecoveryData> mApplicationKeyBlobs;
- private final byte[] mEncryptedRecoveryKeyBlob;
-
- public KeyStoreRecoveryData(int snapshotVersion, @NonNull List<KeyStoreRecoveryMetadata>
- recoveryMetadata, @NonNull List<KeyEntryRecoveryData> applicationKeyBlobs,
- @NonNull byte[] encryptedRecoveryKeyBlob) {
- mSnapshotVersion = snapshotVersion;
- mRecoveryMetadata = Preconditions.checkNotNull(recoveryMetadata);
- mApplicationKeyBlobs = Preconditions.checkNotNull(applicationKeyBlobs);
- mEncryptedRecoveryKeyBlob = Preconditions.checkNotNull(encryptedRecoveryKeyBlob);
- }
-
- /**
- * Snapshot version for given account. It is incremented when user secret or list of application
- * keys changes.
- */
- public int getSnapshotVersion() {
- return mSnapshotVersion;
- }
-
- /**
- * UI and key derivation parameters. Note that combination of secrets may be used.
- */
- public @NonNull List<KeyStoreRecoveryMetadata> getRecoveryMetadata() {
- return mRecoveryMetadata;
- }
-
- /**
- * List of application keys, with key material encrypted by
- * the recovery key ({@link #getEncryptedRecoveryKeyBlob}).
- */
- public @NonNull List<KeyEntryRecoveryData> getApplicationKeyBlobs() {
- return mApplicationKeyBlobs;
- }
-
- /**
- * Recovery key blob, encrypted by user secret and recovery service public key.
- */
- public @NonNull byte[] getEncryptedRecoveryKeyBlob() {
- return mEncryptedRecoveryKeyBlob;
- }
-
- public static final Parcelable.Creator<KeyStoreRecoveryData> CREATOR =
- new Parcelable.Creator<KeyStoreRecoveryData>() {
- public KeyStoreRecoveryData createFromParcel(Parcel in) {
- return new KeyStoreRecoveryData(in);
- }
-
- public KeyStoreRecoveryData[] newArray(int length) {
- return new KeyStoreRecoveryData[length];
- }
- };
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(mSnapshotVersion);
- out.writeTypedList(mRecoveryMetadata);
- out.writeByteArray(mEncryptedRecoveryKeyBlob);
- out.writeTypedList(mApplicationKeyBlobs);
- }
-
- protected KeyStoreRecoveryData(Parcel in) {
- mSnapshotVersion = in.readInt();
- mRecoveryMetadata = in.createTypedArrayList(KeyStoreRecoveryMetadata.CREATOR);
- mEncryptedRecoveryKeyBlob = in.createByteArray();
- mApplicationKeyBlobs = in.createTypedArrayList(KeyEntryRecoveryData.CREATOR);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-}
diff --git a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.aidl b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.aidl
deleted file mode 100644
index e1d49de..0000000
--- a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2017 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.security.recoverablekeystore;
-
-/* @hide */
-parcelable KeyStoreRecoveryMetadata;
diff --git a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.java b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.java
deleted file mode 100644
index 43f9c80..0000000
--- a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2017 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.security.recoverablekeystore;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.util.Preconditions;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
-
-/**
- * Helper class with data necessary to recover Keystore on a new device.
- * It defines UI shown to the user and a way to derive a cryptographic key from user output.
- *
- * @hide
- */
-public final class KeyStoreRecoveryMetadata implements Parcelable {
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({TYPE_LOCKSCREEN, TYPE_CUSTOM_PASSWORD})
- public @interface UserSecretType {
- }
-
- /**
- * Lockscreen secret is required to recover KeyStore.
- */
- public static final int TYPE_LOCKSCREEN = 1;
-
- /**
- * Custom passphrase, unrelated to lock screen, is required to recover KeyStore.
- */
- public static final int TYPE_CUSTOM_PASSWORD = 2;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({TYPE_PIN, TYPE_PASSWORD, TYPE_PATTERN})
- public @interface LockScreenUiFormat {
- }
-
- /**
- * Pin with digits only.
- */
- public static final int TYPE_PIN = 1;
-
- /**
- * Password. String with latin-1 characters only.
- */
- public static final int TYPE_PASSWORD = 2;
-
- /**
- * Pattern with 3 by 3 grid.
- */
- public static final int TYPE_PATTERN = 3;
-
- @UserSecretType
- private final int mUserSecretType;
-
- @LockScreenUiFormat
- private final int mLockScreenUiFormat;
-
- /**
- * Parameters of key derivation function, including algorithm, difficulty, salt.
- */
- private KeyDerivationParameters mKeyDerivationParameters;
- private byte[] mSecret; // Derived from user secret. The field must have limited visibility.
-
- /**
- * @param secret Constructor creates a reference to the secret. Caller must use
- * @link {#clearSecret} to overwrite its value in memory.
- */
- public KeyStoreRecoveryMetadata(@UserSecretType int userSecretType,
- @LockScreenUiFormat int lockScreenUiFormat,
- @NonNull KeyDerivationParameters keyDerivationParameters, @NonNull byte[] secret) {
- mUserSecretType = userSecretType;
- mLockScreenUiFormat = lockScreenUiFormat;
- mKeyDerivationParameters = Preconditions.checkNotNull(keyDerivationParameters);
- mSecret = Preconditions.checkNotNull(secret);
- }
-
- /**
- * Specifies UX shown to user during recovery.
- *
- * @see KeyStore.TYPE_PIN
- * @see KeyStore.TYPE_PASSWORD
- * @see KeyStore.TYPE_PATTERN
- */
- public @LockScreenUiFormat int getLockScreenUiFormat() {
- return mLockScreenUiFormat;
- }
-
- /**
- * Specifies function used to derive symmetric key from user input
- * Format is defined in separate util class.
- */
- public @NonNull KeyDerivationParameters getKeyDerivationParameters() {
- return mKeyDerivationParameters;
- }
-
- /**
- * Secret string derived from user input.
- */
- public @NonNull byte[] getSecret() {
- return mSecret;
- }
-
- /**
- * @see KeyStore.TYPE_LOCKSCREEN
- * @see KeyStore.TYPE_CUSTOM_PASSWORD
- */
- public @UserSecretType int getUserSecretType() {
- return mUserSecretType;
- }
-
- /**
- * Removes secret from memory than object is no longer used.
- * Since finalizer call is not reliable, please use @link {#clearSecret} directly.
- */
- @Override
- protected void finalize() throws Throwable {
- clearSecret();
- super.finalize();
- }
-
- /**
- * Fills mSecret with zeroes.
- */
- public void clearSecret() {
- Arrays.fill(mSecret, (byte) 0);
- }
-
- public static final Parcelable.Creator<KeyStoreRecoveryMetadata> CREATOR =
- new Parcelable.Creator<KeyStoreRecoveryMetadata>() {
- public KeyStoreRecoveryMetadata createFromParcel(Parcel in) {
- return new KeyStoreRecoveryMetadata(in);
- }
-
- public KeyStoreRecoveryMetadata[] newArray(int length) {
- return new KeyStoreRecoveryMetadata[length];
- }
- };
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(mUserSecretType);
- out.writeInt(mLockScreenUiFormat);
- out.writeTypedObject(mKeyDerivationParameters, flags);
- out.writeByteArray(mSecret);
- }
-
- protected KeyStoreRecoveryMetadata(Parcel in) {
- mUserSecretType = in.readInt();
- mLockScreenUiFormat = in.readInt();
- mKeyDerivationParameters = in.readTypedObject(KeyDerivationParameters.CREATOR);
- mSecret = in.createByteArray();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-}
diff --git a/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java b/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java
deleted file mode 100644
index b5ec795..0000000
--- a/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java
+++ /dev/null
@@ -1,493 +0,0 @@
-/*
- * Copyright (C) 2017 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.security.recoverablekeystore;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.PendingIntent;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.ServiceSpecificException;
-import android.security.KeyStore;
-import android.util.AndroidException;
-
-import com.android.internal.widget.ILockSettings;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * A wrapper around KeyStore which lets key be exported to trusted hardware on server side and
- * recovered later.
- *
- * @hide
- */
-public class RecoverableKeyStoreLoader {
-
- public static final String PERMISSION_RECOVER_KEYSTORE = "android.permission.RECOVER_KEYSTORE";
-
- public static final int NO_ERROR = KeyStore.NO_ERROR;
- public static final int SYSTEM_ERROR = KeyStore.SYSTEM_ERROR;
-
- /**
- * Failed because the loader has not been initialized with a recovery public key yet.
- */
- public static final int ERROR_UNINITIALIZED_RECOVERY_PUBLIC_KEY = 20;
-
- /**
- * Failed because no snapshot is yet pending to be synced for the user.
- */
- public static final int ERROR_NO_SNAPSHOT_PENDING = 21;
-
- /**
- * Failed due to an error internal to AndroidKeyStore.
- */
- public static final int ERROR_KEYSTORE_INTERNAL_ERROR = 22;
-
- /**
- * Failed because the user does not have a lock screen set.
- */
- public static final int ERROR_INSECURE_USER = 24;
-
- /**
- * Failed because of an internal database error.
- */
- public static final int ERROR_DATABASE_ERROR = 25;
-
- /**
- * Failed because the provided certificate was not a valid X509 certificate.
- */
- public static final int ERROR_BAD_X509_CERTIFICATE = 26;
-
- /**
- * Should never be thrown - some algorithm that all AOSP implementations must support is
- * not available.
- */
- public static final int ERROR_UNEXPECTED_MISSING_ALGORITHM = 27;
-
- /**
- * The caller is attempting to perform an operation that is not yet fully supported in the API.
- */
- public static final int ERROR_NOT_YET_SUPPORTED = 28;
-
- /**
- * Error thrown if decryption failed. This might be because the tag is wrong, the key is wrong,
- * the data has become corrupted, the data has been tampered with, etc.
- */
- public static final int ERROR_DECRYPTION_FAILED = 29;
-
- /**
- * Rate limit is enforced to prevent using too many trusted remote devices, since each device
- * can have its own number of user secret guesses allowed.
- *
- * @hide
- */
- public static final int ERROR_RATE_LIMIT_EXCEEDED = 30;
-
- /** Key has been successfully synced. */
- public static final int RECOVERY_STATUS_SYNCED = 0;
- /** Waiting for recovery agent to sync the key. */
- public static final int RECOVERY_STATUS_SYNC_IN_PROGRESS = 1;
- /** Recovery account is not available. */
- public static final int RECOVERY_STATUS_MISSING_ACCOUNT = 2;
- /** Key cannot be synced. */
- public static final int RECOVERY_STATUS_PERMANENT_FAILURE = 3;
-
- private final ILockSettings mBinder;
-
- private RecoverableKeyStoreLoader(ILockSettings binder) {
- mBinder = binder;
- }
-
- /** @hide */
- public static RecoverableKeyStoreLoader getInstance() {
- ILockSettings lockSettings =
- ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"));
- return new RecoverableKeyStoreLoader(lockSettings);
- }
-
- /**
- * Exceptions returned by {@link RecoverableKeyStoreLoader}.
- *
- * @hide
- */
- public static class RecoverableKeyStoreLoaderException extends AndroidException {
- private int mErrorCode;
-
- /**
- * Creates new {@link #RecoverableKeyStoreLoaderException} instance from the error code.
- *
- * @param errorCode An error code, as listed at the top of this file.
- * @param message The associated error message.
- * @hide
- */
- public static RecoverableKeyStoreLoaderException fromErrorCode(
- int errorCode, String message) {
- return new RecoverableKeyStoreLoaderException(errorCode, message);
- }
-
- /**
- * Creates new {@link #RecoverableKeyStoreLoaderException} from {@link
- * ServiceSpecificException}.
- *
- * @param e exception thrown on service side.
- * @hide
- */
- static RecoverableKeyStoreLoaderException fromServiceSpecificException(
- ServiceSpecificException e) throws RecoverableKeyStoreLoaderException {
- throw RecoverableKeyStoreLoaderException.fromErrorCode(e.errorCode, e.getMessage());
- }
-
- private RecoverableKeyStoreLoaderException(int errorCode, String message) {
- super(message);
- mErrorCode = errorCode;
- }
-
- /** Returns errorCode. */
- public int getErrorCode() {
- return mErrorCode;
- }
- }
-
- /**
- * Initializes key recovery service for the calling application. RecoverableKeyStoreLoader
- * randomly chooses one of the keys from the list and keeps it to use for future key export
- * operations. Collection of all keys in the list must be signed by the provided {@code
- * rootCertificateAlias}, which must also be present in the list of root certificates
- * preinstalled on the device. The random selection allows RecoverableKeyStoreLoader to select
- * which of a set of remote recovery service devices will be used.
- *
- * <p>In addition, RecoverableKeyStoreLoader enforces a delay of three months between
- * consecutive initialization attempts, to limit the ability of an attacker to often switch
- * remote recovery devices and significantly increase number of recovery attempts.
- *
- * @param rootCertificateAlias alias of a root certificate preinstalled on the device
- * @param signedPublicKeyList binary blob a list of X509 certificates and signature
- * @throws RecoverableKeyStoreLoaderException if signature is invalid, or key rotation was rate
- * limited.
- * @hide
- */
- public void initRecoveryService(
- @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList)
- throws RecoverableKeyStoreLoaderException {
- try {
- mBinder.initRecoveryService(rootCertificateAlias, signedPublicKeyList);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
- }
- }
-
- /**
- * Returns data necessary to store all recoverable keys for given account. Key material is
- * encrypted with user secret and recovery public key.
- *
- * @param account specific to Recovery agent.
- * @return Data necessary to recover keystore.
- * @hide
- */
- public @NonNull KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account)
- throws RecoverableKeyStoreLoaderException {
- try {
- KeyStoreRecoveryData recoveryData = mBinder.getRecoveryData(account);
- return recoveryData;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
- }
- }
-
- /**
- * Sets a listener which notifies recovery agent that new recovery snapshot is available. {@link
- * #getRecoveryData} can be used to get the snapshot. Note that every recovery agent can have at
- * most one registered listener at any time.
- *
- * @param intent triggered when new snapshot is available. Unregisters listener if the value is
- * {@code null}.
- * @hide
- */
- public void setSnapshotCreatedPendingIntent(@Nullable PendingIntent intent)
- throws RecoverableKeyStoreLoaderException {
- try {
- mBinder.setSnapshotCreatedPendingIntent(intent);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
- }
- }
-
- /**
- * Returns a map from recovery agent accounts to corresponding KeyStore recovery snapshot
- * version. Version zero is used, if no snapshots were created for the account.
- *
- * @return Map from recovery agent accounts to snapshot versions.
- * @see KeyStoreRecoveryData#getSnapshotVersion
- * @hide
- */
- public @NonNull Map<byte[], Integer> getRecoverySnapshotVersions()
- throws RecoverableKeyStoreLoaderException {
- try {
- // IPC doesn't support generic Maps.
- @SuppressWarnings("unchecked")
- Map<byte[], Integer> result =
- (Map<byte[], Integer>) mBinder.getRecoverySnapshotVersions();
- return result;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
- }
- }
-
- /**
- * Server parameters used to generate new recovery key blobs. This value will be included in
- * {@code KeyStoreRecoveryData.getEncryptedRecoveryKeyBlob()}. The same value must be included
- * in vaultParams {@link #startRecoverySession}
- *
- * @param serverParameters included in recovery key blob.
- * @see #getRecoveryData
- * @throws RecoverableKeyStoreLoaderException If parameters rotation is rate limited.
- * @hide
- */
- public void setServerParameters(long serverParameters)
- throws RecoverableKeyStoreLoaderException {
- try {
- mBinder.setServerParameters(serverParameters);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
- }
- }
-
- /**
- * Updates recovery status for given keys. It is used to notify keystore that key was
- * successfully stored on the server or there were an error. Application can check this value
- * using {@code getRecoveyStatus}.
- *
- * @param packageName Application whose recoverable keys' statuses are to be updated.
- * @param aliases List of application-specific key aliases. If the array is empty, updates the
- * status for all existing recoverable keys.
- * @param status Status specific to recovery agent.
- */
- public void setRecoveryStatus(
- @NonNull String packageName, @Nullable String[] aliases, int status)
- throws NameNotFoundException, RecoverableKeyStoreLoaderException {
- try {
- mBinder.setRecoveryStatus(packageName, aliases, status);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
- }
- }
-
- /**
- * Returns a {@code Map} from Application's KeyStore key aliases to their recovery status.
- * Negative status values are reserved for recovery agent specific codes. List of common codes:
- *
- * <ul>
- * <li>{@link #RECOVERY_STATUS_SYNCED}
- * <li>{@link #RECOVERY_STATUS_SYNC_IN_PROGRESS}
- * <li>{@link #RECOVERY_STATUS_MISSING_ACCOUNT}
- * <li>{@link #RECOVERY_STATUS_PERMANENT_FAILURE}
- * </ul>
- *
- * @param packageName Application whose recoverable keys' statuses are to be retrieved. if
- * {@code null} caller's package will be used.
- * @return {@code Map} from KeyStore alias to recovery status.
- * @see #setRecoveryStatus
- * @hide
- */
- public Map<String, Integer> getRecoveryStatus(@Nullable String packageName)
- throws RecoverableKeyStoreLoaderException {
- try {
- // IPC doesn't support generic Maps.
- @SuppressWarnings("unchecked")
- Map<String, Integer> result =
- (Map<String, Integer>)
- mBinder.getRecoveryStatus(packageName);
- return result;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
- }
- }
-
- /**
- * Specifies a set of secret types used for end-to-end keystore encryption. Knowing all of them
- * is necessary to recover data.
- *
- * @param secretTypes {@link KeyStoreRecoveryMetadata#TYPE_LOCKSCREEN} or {@link
- * KeyStoreRecoveryMetadata#TYPE_CUSTOM_PASSWORD}
- */
- public void setRecoverySecretTypes(
- @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] secretTypes)
- throws RecoverableKeyStoreLoaderException {
- try {
- mBinder.setRecoverySecretTypes(secretTypes);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
- }
- }
-
- /**
- * Defines a set of secret types used for end-to-end keystore encryption. Knowing all of them is
- * necessary to generate KeyStoreRecoveryData.
- *
- * @return list of recovery secret types
- * @see KeyStoreRecoveryData
- */
- public @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] getRecoverySecretTypes()
- throws RecoverableKeyStoreLoaderException {
- try {
- return mBinder.getRecoverySecretTypes();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
- }
- }
-
- /**
- * Returns a list of recovery secret types, necessary to create a pending recovery snapshot.
- * When user enters a secret of a pending type {@link #recoverySecretAvailable} should be
- * called.
- *
- * @return list of recovery secret types
- */
- public @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] getPendingRecoverySecretTypes()
- throws RecoverableKeyStoreLoaderException {
- try {
- return mBinder.getPendingRecoverySecretTypes();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
- }
- }
-
- /**
- * Method notifies KeyStore that a user-generated secret is available. This method generates a
- * symmetric session key which a trusted remote device can use to return a recovery key. Caller
- * should use {@link KeyStoreRecoveryMetadata#clearSecret} to override the secret value in
- * memory.
- *
- * @param recoverySecret user generated secret together with parameters necessary to regenerate
- * it on a new device.
- */
- public void recoverySecretAvailable(@NonNull KeyStoreRecoveryMetadata recoverySecret)
- throws RecoverableKeyStoreLoaderException {
- try {
- mBinder.recoverySecretAvailable(recoverySecret);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
- }
- }
-
- /**
- * Initializes recovery session and returns a blob with proof of recovery secrets possession.
- * The method generates symmetric key for a session, which trusted remote device can use to
- * return recovery key.
- *
- * @param sessionId ID for recovery session.
- * @param verifierPublicKey Certificate with Public key used to create the recovery blob on the
- * source device. Keystore will verify the certificate using root of trust.
- * @param vaultParams Must match the parameters in the corresponding field in the recovery blob.
- * Used to limit number of guesses.
- * @param vaultChallenge Data passed from server for this recovery session and used to prevent
- * replay attacks
- * @param secrets Secrets provided by user, the method only uses type and secret fields.
- * @return Binary blob with recovery claim. It is encrypted with verifierPublicKey and contains
- * a proof of user secrets, session symmetric key and parameters necessary to identify the
- * counter with the number of failed recovery attempts.
- */
- public @NonNull byte[] startRecoverySession(
- @NonNull String sessionId,
- @NonNull byte[] verifierPublicKey,
- @NonNull byte[] vaultParams,
- @NonNull byte[] vaultChallenge,
- @NonNull List<KeyStoreRecoveryMetadata> secrets)
- throws RecoverableKeyStoreLoaderException {
- try {
- byte[] recoveryClaim =
- mBinder.startRecoverySession(
- sessionId,
- verifierPublicKey,
- vaultParams,
- vaultChallenge,
- secrets);
- return recoveryClaim;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
- }
- }
-
- /**
- * Imports keys.
- *
- * @param sessionId Id for recovery session, same as in
- * {@link #startRecoverySession(String, byte[], byte[], byte[], List)}.
- * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
- * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
- * and session. KeyStore only uses package names from the application info in {@link
- * KeyEntryRecoveryData}. Caller is responsibility to perform certificates check.
- * @return Map from alias to raw key material.
- */
- public Map<String, byte[]> recoverKeys(
- @NonNull String sessionId,
- @NonNull byte[] recoveryKeyBlob,
- @NonNull List<KeyEntryRecoveryData> applicationKeys)
- throws RecoverableKeyStoreLoaderException {
- try {
- return (Map<String, byte[]>) mBinder.recoverKeys(
- sessionId, recoveryKeyBlob, applicationKeys);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
- }
- }
-
- /**
- * Generates a key called {@code alias} and loads it into the recoverable key store. Returns the
- * raw material of the key.
- *
- * @throws RecoverableKeyStoreLoaderException if an error occurred generating and storing the
- * key.
- */
- public byte[] generateAndStoreKey(String alias) throws RecoverableKeyStoreLoaderException {
- try {
- return mBinder.generateAndStoreKey(alias);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
- }
- }
-}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/core/java/android/service/euicc/EuiccProfileInfo.aidl
similarity index 81%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to core/java/android/service/euicc/EuiccProfileInfo.aidl
index d750363..321021b 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/core/java/android/service/euicc/EuiccProfileInfo.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.service.euicc;
-/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+parcelable EuiccProfileInfo;
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 18d4a1e..b7b2b2d 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -55,6 +55,7 @@
import android.widget.RemoteViews;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import java.lang.annotation.Retention;
@@ -890,6 +891,8 @@
createLegacyIconExtras(notification);
// populate remote views for older clients.
maybePopulateRemoteViews(notification);
+ // populate people for older clients.
+ maybePopulatePeople(notification);
} catch (IllegalArgumentException e) {
if (corruptNotifications == null) {
corruptNotifications = new ArrayList<>(N);
@@ -1178,6 +1181,25 @@
}
}
+ /**
+ * Populates remote views for pre-P targeting apps.
+ */
+ private void maybePopulatePeople(Notification notification) {
+ if (getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P) {
+ ArrayList<Notification.Person> people = notification.extras.getParcelableArrayList(
+ Notification.EXTRA_PEOPLE_LIST);
+ if (people != null && people.isEmpty()) {
+ int size = people.size();
+ String[] peopleArray = new String[size];
+ for (int i = 0; i < size; i++) {
+ Notification.Person person = people.get(i);
+ peopleArray[i] = person.resolveToLegacyUri();
+ }
+ notification.extras.putStringArray(Notification.EXTRA_PEOPLE, peopleArray);
+ }
+ }
+ }
+
/** @hide */
protected class NotificationListenerWrapper extends INotificationListener.Stub {
@Override
@@ -1522,7 +1544,11 @@
return mShowBadge;
}
- private void populate(String key, int rank, boolean matchesInterruptionFilter,
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public void populate(String key, int rank, boolean matchesInterruptionFilter,
int visibilityOverride, int suppressedVisualEffects, int importance,
CharSequence explanation, String overrideGroupKey,
NotificationChannel channel, ArrayList<String> overridePeople,
diff --git a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
index 14df7cb..220e498 100644
--- a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
+++ b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
@@ -32,4 +32,5 @@
void isEscrowTokenActive(long handle, int userId);
void removeEscrowToken(long handle, int userId);
void unlockUserWithToken(long handle, in byte[] token, int userId);
+ void showKeyguardErrorMessage(in CharSequence message);
}
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index 4bade9f..40e84b9 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -18,6 +18,7 @@
import android.Manifest;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.app.Service;
@@ -37,6 +38,7 @@
import android.os.UserManager;
import android.util.Log;
import android.util.Slog;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
@@ -301,7 +303,7 @@
public void onDeviceUnlockLockout(long timeoutMs) {
}
- /**
+ /**
* Called when an escrow token is added for user userId.
*
* @param token the added token
@@ -561,6 +563,31 @@
}
}
+ /**
+ * Request showing a transient error message on the keyguard.
+ * The message will be visible on the lock screen or always on display if possible but can be
+ * overridden by other keyguard events of higher priority - eg. fingerprint auth error.
+ * Other trust agents may override your message if posted simultaneously.
+ *
+ * @param message Message to show.
+ */
+ public final void showKeyguardErrorMessage(@NonNull CharSequence message) {
+ if (message == null) {
+ throw new IllegalArgumentException("message cannot be null");
+ }
+ synchronized (mLock) {
+ if (mCallback == null) {
+ Slog.w(TAG, "Cannot show message because service is not connected to framework.");
+ throw new IllegalStateException("Trust agent is not connected");
+ }
+ try {
+ mCallback.showKeyguardErrorMessage(message);
+ } catch (RemoteException e) {
+ onError("calling showKeyguardErrorMessage");
+ }
+ }
+ }
+
@Override
public final IBinder onBind(Intent intent) {
if (DEBUG) Slog.v(TAG, "onBind() intent = " + intent);
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index bf4b6ac..aa97b2a 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1917,10 +1917,10 @@
private static float measurePara(TextPaint paint, CharSequence text, int start, int end,
TextDirectionHeuristic textDir) {
- MeasuredText mt = null;
+ MeasuredParagraph mt = null;
TextLine tl = TextLine.obtain();
try {
- mt = MeasuredText.buildForBidi(text, start, end, textDir, mt);
+ mt = MeasuredParagraph.buildForBidi(text, start, end, textDir, mt);
final char[] chars = mt.getChars();
final int len = chars.length;
final Directions directions = mt.getDirections(0, len);
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
new file mode 100644
index 0000000..c93e036
--- /dev/null
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -0,0 +1,677 @@
+/*
+ * Copyright (C) 2010 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;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Paint;
+import android.text.AutoGrowArray.ByteArray;
+import android.text.AutoGrowArray.FloatArray;
+import android.text.AutoGrowArray.IntArray;
+import android.text.Layout.Directions;
+import android.text.style.MetricAffectingSpan;
+import android.text.style.ReplacementSpan;
+import android.util.Pools.SynchronizedPool;
+
+import dalvik.annotation.optimization.CriticalNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.util.Arrays;
+
+/**
+ * MeasuredParagraph provides text information for rendering purpose.
+ *
+ * The first motivation of this class is identify the text directions and retrieving individual
+ * character widths. However retrieving character widths is slower than identifying text directions.
+ * Thus, this class provides several builder methods for specific purposes.
+ *
+ * - buildForBidi:
+ * Compute only text directions.
+ * - buildForMeasurement:
+ * Compute text direction and all character widths.
+ * - buildForStaticLayout:
+ * This is bit special. StaticLayout also needs to know text direction and character widths for
+ * line breaking, but all things are done in native code. Similarly, text measurement is done
+ * in native code. So instead of storing result to Java array, this keeps the result in native
+ * code since there is no good reason to move the results to Java layer.
+ *
+ * In addition to the character widths, some additional information is computed for each purposes,
+ * e.g. whole text length for measurement or font metrics for static layout.
+ *
+ * MeasuredParagraph is NOT a thread safe object.
+ * @hide
+ */
+public class MeasuredParagraph {
+ private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC';
+
+ private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+ MeasuredParagraph.class.getClassLoader(), nGetReleaseFunc(), 1024);
+
+ private MeasuredParagraph() {} // Use build static functions instead.
+
+ private static final SynchronizedPool<MeasuredParagraph> sPool = new SynchronizedPool<>(1);
+
+ private static @NonNull MeasuredParagraph obtain() { // Use build static functions instead.
+ final MeasuredParagraph mt = sPool.acquire();
+ return mt != null ? mt : new MeasuredParagraph();
+ }
+
+ /**
+ * Recycle the MeasuredParagraph.
+ *
+ * Do not call any methods after you call this method.
+ */
+ public void recycle() {
+ release();
+ sPool.release(this);
+ }
+
+ // The casted original text.
+ //
+ // This may be null if the passed text is not a Spanned.
+ private @Nullable Spanned mSpanned;
+
+ // The start offset of the target range in the original text (mSpanned);
+ private @IntRange(from = 0) int mTextStart;
+
+ // The length of the target range in the original text.
+ private @IntRange(from = 0) int mTextLength;
+
+ // The copied character buffer for measuring text.
+ //
+ // The length of this array is mTextLength.
+ private @Nullable char[] mCopiedBuffer;
+
+ // The whole paragraph direction.
+ private @Layout.Direction int mParaDir;
+
+ // True if the text is LTR direction and doesn't contain any bidi characters.
+ private boolean mLtrWithoutBidi;
+
+ // The bidi level for individual characters.
+ //
+ // This is empty if mLtrWithoutBidi is true.
+ private @NonNull ByteArray mLevels = new ByteArray();
+
+ // The whole width of the text.
+ // See getWholeWidth comments.
+ private @FloatRange(from = 0.0f) float mWholeWidth;
+
+ // Individual characters' widths.
+ // See getWidths comments.
+ private @Nullable FloatArray mWidths = new FloatArray();
+
+ // The span end positions.
+ // See getSpanEndCache comments.
+ private @Nullable IntArray mSpanEndCache = new IntArray(4);
+
+ // The font metrics.
+ // See getFontMetrics comments.
+ private @Nullable IntArray mFontMetrics = new IntArray(4 * 4);
+
+ // The native MeasuredParagraph.
+ // See getNativePtr comments.
+ // Do not modify these members directly. Use bindNativeObject/unbindNativeObject instead.
+ private /* Maybe Zero */ long mNativePtr = 0;
+ private @Nullable Runnable mNativeObjectCleaner;
+
+ // Associate the native object to this Java object.
+ private void bindNativeObject(/* Non Zero*/ long nativePtr) {
+ mNativePtr = nativePtr;
+ mNativeObjectCleaner = sRegistry.registerNativeAllocation(this, nativePtr);
+ }
+
+ // Decouple the native object from this Java object and release the native object.
+ private void unbindNativeObject() {
+ if (mNativePtr != 0) {
+ mNativeObjectCleaner.run();
+ mNativePtr = 0;
+ }
+ }
+
+ // Following two objects are for avoiding object allocation.
+ private @NonNull TextPaint mCachedPaint = new TextPaint();
+ private @Nullable Paint.FontMetricsInt mCachedFm;
+
+ /**
+ * Releases internal buffers.
+ */
+ public void release() {
+ reset();
+ mLevels.clearWithReleasingLargeArray();
+ mWidths.clearWithReleasingLargeArray();
+ mFontMetrics.clearWithReleasingLargeArray();
+ mSpanEndCache.clearWithReleasingLargeArray();
+ }
+
+ /**
+ * Resets the internal state for starting new text.
+ */
+ private void reset() {
+ mSpanned = null;
+ mCopiedBuffer = null;
+ mWholeWidth = 0;
+ mLevels.clear();
+ mWidths.clear();
+ mFontMetrics.clear();
+ mSpanEndCache.clear();
+ unbindNativeObject();
+ }
+
+ /**
+ * Returns the characters to be measured.
+ *
+ * This is always available.
+ */
+ public @NonNull char[] getChars() {
+ return mCopiedBuffer;
+ }
+
+ /**
+ * Returns the paragraph direction.
+ *
+ * This is always available.
+ */
+ public @Layout.Direction int getParagraphDir() {
+ return mParaDir;
+ }
+
+ /**
+ * Returns the directions.
+ *
+ * This is always available.
+ */
+ public Directions getDirections(@IntRange(from = 0) int start, // inclusive
+ @IntRange(from = 0) int end) { // exclusive
+ if (mLtrWithoutBidi) {
+ return Layout.DIRS_ALL_LEFT_TO_RIGHT;
+ }
+
+ final int length = end - start;
+ return AndroidBidi.directions(mParaDir, mLevels.getRawArray(), start, mCopiedBuffer, start,
+ length);
+ }
+
+ /**
+ * Returns the whole text width.
+ *
+ * This is available only if the MeasureText is computed with computeForMeasurement.
+ * Returns 0 in other cases.
+ */
+ public @FloatRange(from = 0.0f) float getWholeWidth() {
+ return mWholeWidth;
+ }
+
+ /**
+ * Returns the individual character's width.
+ *
+ * This is available only if the MeasureText is computed with computeForMeasurement.
+ * Returns empty array in other cases.
+ */
+ public @NonNull FloatArray getWidths() {
+ return mWidths;
+ }
+
+ /**
+ * Returns the MetricsAffectingSpan end indices.
+ *
+ * If the input text is not a spanned string, this has one value that is the length of the text.
+ *
+ * This is available only if the MeasureText is computed with computeForStaticLayout.
+ * Returns empty array in other cases.
+ */
+ public @NonNull IntArray getSpanEndCache() {
+ return mSpanEndCache;
+ }
+
+ /**
+ * Returns the int array which holds FontMetrics.
+ *
+ * This array holds the repeat of top, bottom, ascent, descent of font metrics value.
+ *
+ * This is available only if the MeasureText is computed with computeForStaticLayout.
+ * Returns empty array in other cases.
+ */
+ public @NonNull IntArray getFontMetrics() {
+ return mFontMetrics;
+ }
+
+ /**
+ * Returns the native ptr of the MeasuredParagraph.
+ *
+ * This is available only if the MeasureText is computed with computeForStaticLayout.
+ * Returns 0 in other cases.
+ */
+ public /* Maybe Zero */ long getNativePtr() {
+ return mNativePtr;
+ }
+
+ /**
+ * Generates new MeasuredParagraph for Bidi computation.
+ *
+ * If recycle is null, this returns new instance. If recycle is not null, this fills computed
+ * result to recycle and returns recycle.
+ *
+ * @param text the character sequence to be measured
+ * @param start the inclusive start offset of the target region in the text
+ * @param end the exclusive end offset of the target region in the text
+ * @param textDir the text direction
+ * @param recycle pass existing MeasuredParagraph if you want to recycle it.
+ *
+ * @return measured text
+ */
+ public static @NonNull MeasuredParagraph buildForBidi(@NonNull CharSequence text,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @NonNull TextDirectionHeuristic textDir,
+ @Nullable MeasuredParagraph recycle) {
+ final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
+ mt.resetAndAnalyzeBidi(text, start, end, textDir);
+ return mt;
+ }
+
+ /**
+ * Generates new MeasuredParagraph for measuring texts.
+ *
+ * If recycle is null, this returns new instance. If recycle is not null, this fills computed
+ * result to recycle and returns recycle.
+ *
+ * @param paint the paint to be used for rendering the text.
+ * @param text the character sequence to be measured
+ * @param start the inclusive start offset of the target region in the text
+ * @param end the exclusive end offset of the target region in the text
+ * @param textDir the text direction
+ * @param recycle pass existing MeasuredParagraph if you want to recycle it.
+ *
+ * @return measured text
+ */
+ public static @NonNull MeasuredParagraph buildForMeasurement(@NonNull TextPaint paint,
+ @NonNull CharSequence text,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @NonNull TextDirectionHeuristic textDir,
+ @Nullable MeasuredParagraph recycle) {
+ final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
+ mt.resetAndAnalyzeBidi(text, start, end, textDir);
+
+ mt.mWidths.resize(mt.mTextLength);
+ if (mt.mTextLength == 0) {
+ return mt;
+ }
+
+ if (mt.mSpanned == null) {
+ // No style change by MetricsAffectingSpan. Just measure all text.
+ mt.applyMetricsAffectingSpan(
+ paint, null /* spans */, start, end, 0 /* native static layout ptr */);
+ } else {
+ // There may be a MetricsAffectingSpan. Split into span transitions and apply styles.
+ int spanEnd;
+ for (int spanStart = start; spanStart < end; spanStart = spanEnd) {
+ spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, MetricAffectingSpan.class);
+ MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd,
+ MetricAffectingSpan.class);
+ spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class);
+ mt.applyMetricsAffectingSpan(
+ paint, spans, spanStart, spanEnd, 0 /* native static layout ptr */);
+ }
+ }
+ return mt;
+ }
+
+ /**
+ * Generates new MeasuredParagraph for StaticLayout.
+ *
+ * If recycle is null, this returns new instance. If recycle is not null, this fills computed
+ * result to recycle and returns recycle.
+ *
+ * @param paint the paint to be used for rendering the text.
+ * @param text the character sequence to be measured
+ * @param start the inclusive start offset of the target region in the text
+ * @param end the exclusive end offset of the target region in the text
+ * @param textDir the text direction
+ * @param recycle pass existing MeasuredParagraph if you want to recycle it.
+ *
+ * @return measured text
+ */
+ public static @NonNull MeasuredParagraph buildForStaticLayout(
+ @NonNull TextPaint paint,
+ @NonNull CharSequence text,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @NonNull TextDirectionHeuristic textDir,
+ @Nullable MeasuredParagraph recycle) {
+ final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
+ mt.resetAndAnalyzeBidi(text, start, end, textDir);
+ if (mt.mTextLength == 0) {
+ // Need to build empty native measured text for StaticLayout.
+ // TODO: Stop creating empty measured text for empty lines.
+ long nativeBuilderPtr = nInitBuilder();
+ try {
+ mt.bindNativeObject(
+ nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer));
+ } finally {
+ nFreeBuilder(nativeBuilderPtr);
+ }
+ return mt;
+ }
+
+ long nativeBuilderPtr = nInitBuilder();
+ try {
+ if (mt.mSpanned == null) {
+ // No style change by MetricsAffectingSpan. Just measure all text.
+ mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, nativeBuilderPtr);
+ mt.mSpanEndCache.append(end);
+ } else {
+ // There may be a MetricsAffectingSpan. Split into span transitions and apply
+ // styles.
+ int spanEnd;
+ for (int spanStart = start; spanStart < end; spanStart = spanEnd) {
+ spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end,
+ MetricAffectingSpan.class);
+ MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd,
+ MetricAffectingSpan.class);
+ spans = TextUtils.removeEmptySpans(spans, mt.mSpanned,
+ MetricAffectingSpan.class);
+ mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd,
+ nativeBuilderPtr);
+ mt.mSpanEndCache.append(spanEnd);
+ }
+ }
+ mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer));
+ } finally {
+ nFreeBuilder(nativeBuilderPtr);
+ }
+
+ return mt;
+ }
+
+ /**
+ * Reset internal state and analyzes text for bidirectional runs.
+ *
+ * @param text the character sequence to be measured
+ * @param start the inclusive start offset of the target region in the text
+ * @param end the exclusive end offset of the target region in the text
+ * @param textDir the text direction
+ */
+ private void resetAndAnalyzeBidi(@NonNull CharSequence text,
+ @IntRange(from = 0) int start, // inclusive
+ @IntRange(from = 0) int end, // exclusive
+ @NonNull TextDirectionHeuristic textDir) {
+ reset();
+ mSpanned = text instanceof Spanned ? (Spanned) text : null;
+ mTextStart = start;
+ mTextLength = end - start;
+
+ if (mCopiedBuffer == null || mCopiedBuffer.length != mTextLength) {
+ mCopiedBuffer = new char[mTextLength];
+ }
+ TextUtils.getChars(text, start, end, mCopiedBuffer, 0);
+
+ // Replace characters associated with ReplacementSpan to U+FFFC.
+ if (mSpanned != null) {
+ ReplacementSpan[] spans = mSpanned.getSpans(start, end, ReplacementSpan.class);
+
+ for (int i = 0; i < spans.length; i++) {
+ int startInPara = mSpanned.getSpanStart(spans[i]) - start;
+ int endInPara = mSpanned.getSpanEnd(spans[i]) - start;
+ // The span interval may be larger and must be restricted to [start, end)
+ if (startInPara < 0) startInPara = 0;
+ if (endInPara > mTextLength) endInPara = mTextLength;
+ Arrays.fill(mCopiedBuffer, startInPara, endInPara, OBJECT_REPLACEMENT_CHARACTER);
+ }
+ }
+
+ if ((textDir == TextDirectionHeuristics.LTR
+ || textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR
+ || textDir == TextDirectionHeuristics.ANYRTL_LTR)
+ && TextUtils.doesNotNeedBidi(mCopiedBuffer, 0, mTextLength)) {
+ mLevels.clear();
+ mParaDir = Layout.DIR_LEFT_TO_RIGHT;
+ mLtrWithoutBidi = true;
+ } else {
+ final int bidiRequest;
+ if (textDir == TextDirectionHeuristics.LTR) {
+ bidiRequest = Layout.DIR_REQUEST_LTR;
+ } else if (textDir == TextDirectionHeuristics.RTL) {
+ bidiRequest = Layout.DIR_REQUEST_RTL;
+ } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) {
+ bidiRequest = Layout.DIR_REQUEST_DEFAULT_LTR;
+ } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) {
+ bidiRequest = Layout.DIR_REQUEST_DEFAULT_RTL;
+ } else {
+ final boolean isRtl = textDir.isRtl(mCopiedBuffer, 0, mTextLength);
+ bidiRequest = isRtl ? Layout.DIR_REQUEST_RTL : Layout.DIR_REQUEST_LTR;
+ }
+ mLevels.resize(mTextLength);
+ mParaDir = AndroidBidi.bidi(bidiRequest, mCopiedBuffer, mLevels.getRawArray());
+ mLtrWithoutBidi = false;
+ }
+ }
+
+ private void applyReplacementRun(@NonNull ReplacementSpan replacement,
+ @IntRange(from = 0) int start, // inclusive, in copied buffer
+ @IntRange(from = 0) int end, // exclusive, in copied buffer
+ /* Maybe Zero */ long nativeBuilderPtr) {
+ // Use original text. Shouldn't matter.
+ // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for
+ // backward compatibility? or Should we initialize them for getFontMetricsInt?
+ final float width = replacement.getSize(
+ mCachedPaint, mSpanned, start + mTextStart, end + mTextStart, mCachedFm);
+ if (nativeBuilderPtr == 0) {
+ // Assigns all width to the first character. This is the same behavior as minikin.
+ mWidths.set(start, width);
+ if (end > start + 1) {
+ Arrays.fill(mWidths.getRawArray(), start + 1, end, 0.0f);
+ }
+ mWholeWidth += width;
+ } else {
+ nAddReplacementRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
+ width);
+ }
+ }
+
+ private void applyStyleRun(@IntRange(from = 0) int start, // inclusive, in copied buffer
+ @IntRange(from = 0) int end, // exclusive, in copied buffer
+ /* Maybe Zero */ long nativeBuilderPtr) {
+ if (nativeBuilderPtr != 0) {
+ mCachedPaint.getFontMetricsInt(mCachedFm);
+ }
+
+ if (mLtrWithoutBidi) {
+ // If the whole text is LTR direction, just apply whole region.
+ if (nativeBuilderPtr == 0) {
+ mWholeWidth += mCachedPaint.getTextRunAdvances(
+ mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */,
+ mWidths.getRawArray(), start);
+ } else {
+ nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
+ false /* isRtl */);
+ }
+ } else {
+ // If there is multiple bidi levels, split into individual bidi level and apply style.
+ byte level = mLevels.get(start);
+ // Note that the empty text or empty range won't reach this method.
+ // Safe to search from start + 1.
+ for (int levelStart = start, levelEnd = start + 1;; ++levelEnd) {
+ if (levelEnd == end || mLevels.get(levelEnd) != level) { // transition point
+ final boolean isRtl = (level & 0x1) != 0;
+ if (nativeBuilderPtr == 0) {
+ final int levelLength = levelEnd - levelStart;
+ mWholeWidth += mCachedPaint.getTextRunAdvances(
+ mCopiedBuffer, levelStart, levelLength, levelStart, levelLength,
+ isRtl, mWidths.getRawArray(), levelStart);
+ } else {
+ nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), levelStart,
+ levelEnd, isRtl);
+ }
+ if (levelEnd == end) {
+ break;
+ }
+ levelStart = levelEnd;
+ level = mLevels.get(levelEnd);
+ }
+ }
+ }
+ }
+
+ private void applyMetricsAffectingSpan(
+ @NonNull TextPaint paint,
+ @Nullable MetricAffectingSpan[] spans,
+ @IntRange(from = 0) int start, // inclusive, in original text buffer
+ @IntRange(from = 0) int end, // exclusive, in original text buffer
+ /* Maybe Zero */ long nativeBuilderPtr) {
+ mCachedPaint.set(paint);
+ // XXX paint should not have a baseline shift, but...
+ mCachedPaint.baselineShift = 0;
+
+ final boolean needFontMetrics = nativeBuilderPtr != 0;
+
+ if (needFontMetrics && mCachedFm == null) {
+ mCachedFm = new Paint.FontMetricsInt();
+ }
+
+ ReplacementSpan replacement = null;
+ if (spans != null) {
+ for (int i = 0; i < spans.length; i++) {
+ MetricAffectingSpan span = spans[i];
+ if (span instanceof ReplacementSpan) {
+ // The last ReplacementSpan is effective for backward compatibility reasons.
+ replacement = (ReplacementSpan) span;
+ } else {
+ // TODO: No need to call updateMeasureState for ReplacementSpan as well?
+ span.updateMeasureState(mCachedPaint);
+ }
+ }
+ }
+
+ final int startInCopiedBuffer = start - mTextStart;
+ final int endInCopiedBuffer = end - mTextStart;
+
+ if (replacement != null) {
+ applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer,
+ nativeBuilderPtr);
+ } else {
+ applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, nativeBuilderPtr);
+ }
+
+ if (needFontMetrics) {
+ if (mCachedPaint.baselineShift < 0) {
+ mCachedFm.ascent += mCachedPaint.baselineShift;
+ mCachedFm.top += mCachedPaint.baselineShift;
+ } else {
+ mCachedFm.descent += mCachedPaint.baselineShift;
+ mCachedFm.bottom += mCachedPaint.baselineShift;
+ }
+
+ mFontMetrics.append(mCachedFm.top);
+ mFontMetrics.append(mCachedFm.bottom);
+ mFontMetrics.append(mCachedFm.ascent);
+ mFontMetrics.append(mCachedFm.descent);
+ }
+ }
+
+ /**
+ * Returns the maximum index that the accumulated width not exceeds the width.
+ *
+ * If forward=false is passed, returns the minimum index from the end instead.
+ *
+ * This only works if the MeasuredParagraph is computed with computeForMeasurement.
+ * Undefined behavior in other case.
+ */
+ @IntRange(from = 0) int breakText(int limit, boolean forwards, float width) {
+ float[] w = mWidths.getRawArray();
+ if (forwards) {
+ int i = 0;
+ while (i < limit) {
+ width -= w[i];
+ if (width < 0.0f) break;
+ i++;
+ }
+ while (i > 0 && mCopiedBuffer[i - 1] == ' ') i--;
+ return i;
+ } else {
+ int i = limit - 1;
+ while (i >= 0) {
+ width -= w[i];
+ if (width < 0.0f) break;
+ i--;
+ }
+ while (i < limit - 1 && (mCopiedBuffer[i + 1] == ' ' || w[i + 1] == 0.0f)) {
+ i++;
+ }
+ return limit - i - 1;
+ }
+ }
+
+ /**
+ * Returns the length of the substring.
+ *
+ * This only works if the MeasuredParagraph is computed with computeForMeasurement.
+ * Undefined behavior in other case.
+ */
+ @FloatRange(from = 0.0f) float measure(int start, int limit) {
+ float width = 0;
+ float[] w = mWidths.getRawArray();
+ for (int i = start; i < limit; ++i) {
+ width += w[i];
+ }
+ return width;
+ }
+
+ private static native /* Non Zero */ long nInitBuilder();
+
+ /**
+ * Apply style to make native measured text.
+ *
+ * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
+ * @param paintPtr The native paint pointer to be applied.
+ * @param start The start offset in the copied buffer.
+ * @param end The end offset in the copied buffer.
+ * @param isRtl True if the text is RTL.
+ */
+ private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
+ /* Non Zero */ long paintPtr,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ boolean isRtl);
+
+ /**
+ * Apply ReplacementRun to make native measured text.
+ *
+ * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
+ * @param paintPtr The native paint pointer to be applied.
+ * @param start The start offset in the copied buffer.
+ * @param end The end offset in the copied buffer.
+ * @param width The width of the replacement.
+ */
+ private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr,
+ /* Non Zero */ long paintPtr,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @FloatRange(from = 0) float width);
+
+ private static native long nBuildNativeMeasuredParagraph(/* Non Zero */ long nativeBuilderPtr,
+ @NonNull char[] text);
+
+ private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
+
+ @CriticalNative
+ private static native /* Non Zero */ long nGetReleaseFunc();
+}
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
index 14d6f9e..2c30360 100644
--- a/core/java/android/text/MeasuredText.java
+++ b/core/java/android/text/MeasuredText.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -16,661 +16,255 @@
package android.text;
-import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.Paint;
-import android.text.AutoGrowArray.ByteArray;
-import android.text.AutoGrowArray.FloatArray;
-import android.text.AutoGrowArray.IntArray;
-import android.text.Layout.Directions;
-import android.text.style.MetricAffectingSpan;
-import android.text.style.ReplacementSpan;
-import android.util.Pools.SynchronizedPool;
+import android.util.IntArray;
-import dalvik.annotation.optimization.CriticalNative;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.Preconditions;
-import libcore.util.NativeAllocationRegistry;
-
-import java.util.Arrays;
+import java.util.ArrayList;
/**
- * MeasuredText provides text information for rendering purpose.
- *
- * The first motivation of this class is identify the text directions and retrieving individual
- * character widths. However retrieving character widths is slower than identifying text directions.
- * Thus, this class provides several builder methods for specific purposes.
- *
- * - buildForBidi:
- * Compute only text directions.
- * - buildForMeasurement:
- * Compute text direction and all character widths.
- * - buildForStaticLayout:
- * This is bit special. StaticLayout also needs to know text direction and character widths for
- * line breaking, but all things are done in native code. Similarly, text measurement is done
- * in native code. So instead of storing result to Java array, this keeps the result in native
- * code since there is no good reason to move the results to Java layer.
- *
- * In addition to the character widths, some additional information is computed for each purposes,
- * e.g. whole text length for measurement or font metrics for static layout.
- *
- * MeasuredText is NOT a thread safe object.
- * @hide
+ * A text which has already been measured.
*/
-public class MeasuredText {
- private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC';
+public class MeasuredText implements Spanned {
+ private static final char LINE_FEED = '\n';
- private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
- MeasuredText.class.getClassLoader(), nGetReleaseFunc(), 1024);
+ // The original text.
+ private final @NonNull CharSequence mText;
- private MeasuredText() {} // Use build static functions instead.
+ // The inclusive start offset of the measuring target.
+ private final @IntRange(from = 0) int mStart;
- private static final SynchronizedPool<MeasuredText> sPool = new SynchronizedPool<>(1);
+ // The exclusive end offset of the measuring target.
+ private final @IntRange(from = 0) int mEnd;
- private static @NonNull MeasuredText obtain() { // Use build static functions instead.
- final MeasuredText mt = sPool.acquire();
- return mt != null ? mt : new MeasuredText();
+ // The TextPaint used for measurement.
+ private final @NonNull TextPaint mPaint;
+
+ // The requested text direction.
+ private final @NonNull TextDirectionHeuristic mTextDir;
+
+ // The measured paragraph texts.
+ private final @NonNull MeasuredParagraph[] mMeasuredParagraphs;
+
+ // The sorted paragraph end offsets.
+ private final @NonNull int[] mParagraphBreakPoints;
+
+ /**
+ * Build MeasuredText from the text.
+ *
+ * @param text The text to be measured.
+ * @param paint The paint to be used for drawing.
+ * @param textDir The text direction.
+ * @return The measured text.
+ */
+ public static @NonNull MeasuredText build(@NonNull CharSequence text,
+ @NonNull TextPaint paint,
+ @NonNull TextDirectionHeuristic textDir) {
+ return MeasuredText.build(text, paint, textDir, 0, text.length());
}
/**
- * Recycle the MeasuredText.
+ * Build MeasuredText from the specific range of the text..
*
- * Do not call any methods after you call this method.
+ * @param text The text to be measured.
+ * @param paint The paint to be used for drawing.
+ * @param textDir The text direction.
+ * @param start The inclusive start offset of the text.
+ * @param end The exclusive start offset of the text.
+ * @return The measured text.
*/
- public void recycle() {
- release();
- sPool.release(this);
+ public static @NonNull MeasuredText build(@NonNull CharSequence text,
+ @NonNull TextPaint paint,
+ @NonNull TextDirectionHeuristic textDir,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end) {
+ Preconditions.checkNotNull(text);
+ Preconditions.checkNotNull(paint);
+ Preconditions.checkNotNull(textDir);
+ Preconditions.checkArgumentInRange(start, 0, text.length(), "start");
+ Preconditions.checkArgumentInRange(end, 0, text.length(), "end");
+
+ final IntArray paragraphEnds = new IntArray();
+ final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>();
+
+ int paraEnd = 0;
+ for (int paraStart = start; paraStart < end; paraStart = paraEnd) {
+ paraEnd = TextUtils.indexOf(text, LINE_FEED, paraStart, end);
+ if (paraEnd < 0) {
+ // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph end.
+ paraEnd = end;
+ } else {
+ paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph.
+ }
+
+ paragraphEnds.add(paraEnd);
+ measuredTexts.add(MeasuredParagraph.buildForStaticLayout(
+ paint, text, paraStart, paraEnd, textDir, null /* no recycle */));
+ }
+
+ return new MeasuredText(text, start, end, paint, textDir,
+ measuredTexts.toArray(new MeasuredParagraph[measuredTexts.size()]),
+ paragraphEnds.toArray());
}
- // The casted original text.
+ // Use MeasuredText.build instead.
+ private MeasuredText(@NonNull CharSequence text,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @NonNull TextPaint paint,
+ @NonNull TextDirectionHeuristic textDir,
+ @NonNull MeasuredParagraph[] measuredTexts,
+ @NonNull int[] paragraphBreakPoints) {
+ mText = text;
+ mStart = start;
+ mEnd = end;
+ mPaint = paint;
+ mMeasuredParagraphs = measuredTexts;
+ mParagraphBreakPoints = paragraphBreakPoints;
+ mTextDir = textDir;
+ }
+
+ /**
+ * Return the underlying text.
+ */
+ public @NonNull CharSequence getText() {
+ return mText;
+ }
+
+ /**
+ * Returns the inclusive start offset of measured region.
+ */
+ public @IntRange(from = 0) int getStart() {
+ return mStart;
+ }
+
+ /**
+ * Returns the exclusive end offset of measured region.
+ */
+ public @IntRange(from = 0) int getEnd() {
+ return mEnd;
+ }
+
+ /**
+ * Returns the text direction associated with char sequence.
+ */
+ public @NonNull TextDirectionHeuristic getTextDir() {
+ return mTextDir;
+ }
+
+ /**
+ * Returns the paint used to measure this text.
+ */
+ public @NonNull TextPaint getPaint() {
+ return mPaint;
+ }
+
+ /**
+ * Returns the length of the paragraph of this text.
+ */
+ public @IntRange(from = 0) int getParagraphCount() {
+ return mParagraphBreakPoints.length;
+ }
+
+ /**
+ * Returns the paragraph start offset of the text.
+ */
+ public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) {
+ Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
+ return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1];
+ }
+
+ /**
+ * Returns the paragraph end offset of the text.
+ */
+ public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) {
+ Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
+ return mParagraphBreakPoints[paraIndex];
+ }
+
+ /** @hide */
+ public @NonNull MeasuredParagraph getMeasuredParagraph(@IntRange(from = 0) int paraIndex) {
+ return mMeasuredParagraphs[paraIndex];
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Spanned overrides
//
- // This may be null if the passed text is not a Spanned.
- private @Nullable Spanned mSpanned;
+ // Just proxy for underlying mText if appropriate.
- // The start offset of the target range in the original text (mSpanned);
- private @IntRange(from = 0) int mTextStart;
+ @Override
+ public <T> T[] getSpans(int start, int end, Class<T> type) {
+ if (mText instanceof Spanned) {
+ return ((Spanned) mText).getSpans(start, end, type);
+ } else {
+ return ArrayUtils.emptyArray(type);
+ }
+ }
- // The length of the target range in the original text.
- private @IntRange(from = 0) int mTextLength;
+ @Override
+ public int getSpanStart(Object tag) {
+ if (mText instanceof Spanned) {
+ return ((Spanned) mText).getSpanStart(tag);
+ } else {
+ return -1;
+ }
+ }
- // The copied character buffer for measuring text.
+ @Override
+ public int getSpanEnd(Object tag) {
+ if (mText instanceof Spanned) {
+ return ((Spanned) mText).getSpanEnd(tag);
+ } else {
+ return -1;
+ }
+ }
+
+ @Override
+ public int getSpanFlags(Object tag) {
+ if (mText instanceof Spanned) {
+ return ((Spanned) mText).getSpanFlags(tag);
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public int nextSpanTransition(int start, int limit, Class type) {
+ if (mText instanceof Spanned) {
+ return ((Spanned) mText).nextSpanTransition(start, limit, type);
+ } else {
+ return mText.length();
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // CharSequence overrides.
//
- // The length of this array is mTextLength.
- private @Nullable char[] mCopiedBuffer;
+ // Just proxy for underlying mText.
- // The whole paragraph direction.
- private @Layout.Direction int mParaDir;
-
- // True if the text is LTR direction and doesn't contain any bidi characters.
- private boolean mLtrWithoutBidi;
-
- // The bidi level for individual characters.
- //
- // This is empty if mLtrWithoutBidi is true.
- private @NonNull ByteArray mLevels = new ByteArray();
-
- // The whole width of the text.
- // See getWholeWidth comments.
- private @FloatRange(from = 0.0f) float mWholeWidth;
-
- // Individual characters' widths.
- // See getWidths comments.
- private @Nullable FloatArray mWidths = new FloatArray();
-
- // The span end positions.
- // See getSpanEndCache comments.
- private @Nullable IntArray mSpanEndCache = new IntArray(4);
-
- // The font metrics.
- // See getFontMetrics comments.
- private @Nullable IntArray mFontMetrics = new IntArray(4 * 4);
-
- // The native MeasuredText.
- // See getNativePtr comments.
- // Do not modify these members directly. Use bindNativeObject/unbindNativeObject instead.
- private /* Maybe Zero */ long mNativePtr = 0;
- private @Nullable Runnable mNativeObjectCleaner;
-
- // Associate the native object to this Java object.
- private void bindNativeObject(/* Non Zero*/ long nativePtr) {
- mNativePtr = nativePtr;
- mNativeObjectCleaner = sRegistry.registerNativeAllocation(this, nativePtr);
+ @Override
+ public int length() {
+ return mText.length();
}
- // Decouple the native object from this Java object and release the native object.
- private void unbindNativeObject() {
- if (mNativePtr != 0) {
- mNativeObjectCleaner.run();
- mNativePtr = 0;
- }
+ @Override
+ public char charAt(int index) {
+ // TODO: Should this be index + mStart ?
+ return mText.charAt(index);
}
- // Following two objects are for avoiding object allocation.
- private @NonNull TextPaint mCachedPaint = new TextPaint();
- private @Nullable Paint.FontMetricsInt mCachedFm;
-
- /**
- * Releases internal buffers.
- */
- public void release() {
- reset();
- mLevels.clearWithReleasingLargeArray();
- mWidths.clearWithReleasingLargeArray();
- mFontMetrics.clearWithReleasingLargeArray();
- mSpanEndCache.clearWithReleasingLargeArray();
+ @Override
+ public CharSequence subSequence(int start, int end) {
+ // TODO: return MeasuredText.
+ // TODO: Should this be index + mStart, end + mStart ?
+ return mText.subSequence(start, end);
}
- /**
- * Resets the internal state for starting new text.
- */
- private void reset() {
- mSpanned = null;
- mCopiedBuffer = null;
- mWholeWidth = 0;
- mLevels.clear();
- mWidths.clear();
- mFontMetrics.clear();
- mSpanEndCache.clear();
- unbindNativeObject();
+ @Override
+ public String toString() {
+ return mText.toString();
}
-
- /**
- * Returns the characters to be measured.
- *
- * This is always available.
- */
- public @NonNull char[] getChars() {
- return mCopiedBuffer;
- }
-
- /**
- * Returns the paragraph direction.
- *
- * This is always available.
- */
- public @Layout.Direction int getParagraphDir() {
- return mParaDir;
- }
-
- /**
- * Returns the directions.
- *
- * This is always available.
- */
- public Directions getDirections(@IntRange(from = 0) int start, // inclusive
- @IntRange(from = 0) int end) { // exclusive
- if (mLtrWithoutBidi) {
- return Layout.DIRS_ALL_LEFT_TO_RIGHT;
- }
-
- final int length = end - start;
- return AndroidBidi.directions(mParaDir, mLevels.getRawArray(), start, mCopiedBuffer, start,
- length);
- }
-
- /**
- * Returns the whole text width.
- *
- * This is available only if the MeasureText is computed with computeForMeasurement.
- * Returns 0 in other cases.
- */
- public @FloatRange(from = 0.0f) float getWholeWidth() {
- return mWholeWidth;
- }
-
- /**
- * Returns the individual character's width.
- *
- * This is available only if the MeasureText is computed with computeForMeasurement.
- * Returns empty array in other cases.
- */
- public @NonNull FloatArray getWidths() {
- return mWidths;
- }
-
- /**
- * Returns the MetricsAffectingSpan end indices.
- *
- * If the input text is not a spanned string, this has one value that is the length of the text.
- *
- * This is available only if the MeasureText is computed with computeForStaticLayout.
- * Returns empty array in other cases.
- */
- public @NonNull IntArray getSpanEndCache() {
- return mSpanEndCache;
- }
-
- /**
- * Returns the int array which holds FontMetrics.
- *
- * This array holds the repeat of top, bottom, ascent, descent of font metrics value.
- *
- * This is available only if the MeasureText is computed with computeForStaticLayout.
- * Returns empty array in other cases.
- */
- public @NonNull IntArray getFontMetrics() {
- return mFontMetrics;
- }
-
- /**
- * Returns the native ptr of the MeasuredText.
- *
- * This is available only if the MeasureText is computed with computeForStaticLayout.
- * Returns 0 in other cases.
- */
- public /* Maybe Zero */ long getNativePtr() {
- return mNativePtr;
- }
-
- /**
- * Generates new MeasuredText for Bidi computation.
- *
- * If recycle is null, this returns new instance. If recycle is not null, this fills computed
- * result to recycle and returns recycle.
- *
- * @param text the character sequence to be measured
- * @param start the inclusive start offset of the target region in the text
- * @param end the exclusive end offset of the target region in the text
- * @param textDir the text direction
- * @param recycle pass existing MeasuredText if you want to recycle it.
- *
- * @return measured text
- */
- public static @NonNull MeasuredText buildForBidi(@NonNull CharSequence text,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- @NonNull TextDirectionHeuristic textDir,
- @Nullable MeasuredText recycle) {
- final MeasuredText mt = recycle == null ? obtain() : recycle;
- mt.resetAndAnalyzeBidi(text, start, end, textDir);
- return mt;
- }
-
- /**
- * Generates new MeasuredText for measuring texts.
- *
- * If recycle is null, this returns new instance. If recycle is not null, this fills computed
- * result to recycle and returns recycle.
- *
- * @param paint the paint to be used for rendering the text.
- * @param text the character sequence to be measured
- * @param start the inclusive start offset of the target region in the text
- * @param end the exclusive end offset of the target region in the text
- * @param textDir the text direction
- * @param recycle pass existing MeasuredText if you want to recycle it.
- *
- * @return measured text
- */
- public static @NonNull MeasuredText buildForMeasurement(@NonNull TextPaint paint,
- @NonNull CharSequence text,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- @NonNull TextDirectionHeuristic textDir,
- @Nullable MeasuredText recycle) {
- final MeasuredText mt = recycle == null ? obtain() : recycle;
- mt.resetAndAnalyzeBidi(text, start, end, textDir);
-
- mt.mWidths.resize(mt.mTextLength);
- if (mt.mTextLength == 0) {
- return mt;
- }
-
- if (mt.mSpanned == null) {
- // No style change by MetricsAffectingSpan. Just measure all text.
- mt.applyMetricsAffectingSpan(
- paint, null /* spans */, start, end, 0 /* native static layout ptr */);
- } else {
- // There may be a MetricsAffectingSpan. Split into span transitions and apply styles.
- int spanEnd;
- for (int spanStart = start; spanStart < end; spanStart = spanEnd) {
- spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, MetricAffectingSpan.class);
- MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd,
- MetricAffectingSpan.class);
- spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class);
- mt.applyMetricsAffectingSpan(
- paint, spans, spanStart, spanEnd, 0 /* native static layout ptr */);
- }
- }
- return mt;
- }
-
- /**
- * Generates new MeasuredText for StaticLayout.
- *
- * If recycle is null, this returns new instance. If recycle is not null, this fills computed
- * result to recycle and returns recycle.
- *
- * @param paint the paint to be used for rendering the text.
- * @param text the character sequence to be measured
- * @param start the inclusive start offset of the target region in the text
- * @param end the exclusive end offset of the target region in the text
- * @param textDir the text direction
- * @param recycle pass existing MeasuredText if you want to recycle it.
- *
- * @return measured text
- */
- public static @NonNull MeasuredText buildForStaticLayout(
- @NonNull TextPaint paint,
- @NonNull CharSequence text,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- @NonNull TextDirectionHeuristic textDir,
- @Nullable MeasuredText recycle) {
- final MeasuredText mt = recycle == null ? obtain() : recycle;
- mt.resetAndAnalyzeBidi(text, start, end, textDir);
- if (mt.mTextLength == 0) {
- // Need to build empty native measured text for StaticLayout.
- // TODO: Stop creating empty measured text for empty lines.
- long nativeBuilderPtr = nInitBuilder();
- try {
- mt.bindNativeObject(nBuildNativeMeasuredText(nativeBuilderPtr, mt.mCopiedBuffer));
- } finally {
- nFreeBuilder(nativeBuilderPtr);
- }
- return mt;
- }
-
- long nativeBuilderPtr = nInitBuilder();
- try {
- if (mt.mSpanned == null) {
- // No style change by MetricsAffectingSpan. Just measure all text.
- mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, nativeBuilderPtr);
- mt.mSpanEndCache.append(end);
- } else {
- // There may be a MetricsAffectingSpan. Split into span transitions and apply
- // styles.
- int spanEnd;
- for (int spanStart = start; spanStart < end; spanStart = spanEnd) {
- spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end,
- MetricAffectingSpan.class);
- MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd,
- MetricAffectingSpan.class);
- spans = TextUtils.removeEmptySpans(spans, mt.mSpanned,
- MetricAffectingSpan.class);
- mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd,
- nativeBuilderPtr);
- mt.mSpanEndCache.append(spanEnd);
- }
- }
- mt.bindNativeObject(nBuildNativeMeasuredText(nativeBuilderPtr, mt.mCopiedBuffer));
- } finally {
- nFreeBuilder(nativeBuilderPtr);
- }
-
- return mt;
- }
-
- /**
- * Reset internal state and analyzes text for bidirectional runs.
- *
- * @param text the character sequence to be measured
- * @param start the inclusive start offset of the target region in the text
- * @param end the exclusive end offset of the target region in the text
- * @param textDir the text direction
- */
- private void resetAndAnalyzeBidi(@NonNull CharSequence text,
- @IntRange(from = 0) int start, // inclusive
- @IntRange(from = 0) int end, // exclusive
- @NonNull TextDirectionHeuristic textDir) {
- reset();
- mSpanned = text instanceof Spanned ? (Spanned) text : null;
- mTextStart = start;
- mTextLength = end - start;
-
- if (mCopiedBuffer == null || mCopiedBuffer.length != mTextLength) {
- mCopiedBuffer = new char[mTextLength];
- }
- TextUtils.getChars(text, start, end, mCopiedBuffer, 0);
-
- // Replace characters associated with ReplacementSpan to U+FFFC.
- if (mSpanned != null) {
- ReplacementSpan[] spans = mSpanned.getSpans(start, end, ReplacementSpan.class);
-
- for (int i = 0; i < spans.length; i++) {
- int startInPara = mSpanned.getSpanStart(spans[i]) - start;
- int endInPara = mSpanned.getSpanEnd(spans[i]) - start;
- // The span interval may be larger and must be restricted to [start, end)
- if (startInPara < 0) startInPara = 0;
- if (endInPara > mTextLength) endInPara = mTextLength;
- Arrays.fill(mCopiedBuffer, startInPara, endInPara, OBJECT_REPLACEMENT_CHARACTER);
- }
- }
-
- if ((textDir == TextDirectionHeuristics.LTR ||
- textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR ||
- textDir == TextDirectionHeuristics.ANYRTL_LTR) &&
- TextUtils.doesNotNeedBidi(mCopiedBuffer, 0, mTextLength)) {
- mLevels.clear();
- mParaDir = Layout.DIR_LEFT_TO_RIGHT;
- mLtrWithoutBidi = true;
- } else {
- final int bidiRequest;
- if (textDir == TextDirectionHeuristics.LTR) {
- bidiRequest = Layout.DIR_REQUEST_LTR;
- } else if (textDir == TextDirectionHeuristics.RTL) {
- bidiRequest = Layout.DIR_REQUEST_RTL;
- } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) {
- bidiRequest = Layout.DIR_REQUEST_DEFAULT_LTR;
- } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) {
- bidiRequest = Layout.DIR_REQUEST_DEFAULT_RTL;
- } else {
- final boolean isRtl = textDir.isRtl(mCopiedBuffer, 0, mTextLength);
- bidiRequest = isRtl ? Layout.DIR_REQUEST_RTL : Layout.DIR_REQUEST_LTR;
- }
- mLevels.resize(mTextLength);
- mParaDir = AndroidBidi.bidi(bidiRequest, mCopiedBuffer, mLevels.getRawArray());
- mLtrWithoutBidi = false;
- }
- }
-
- private void applyReplacementRun(@NonNull ReplacementSpan replacement,
- @IntRange(from = 0) int start, // inclusive, in copied buffer
- @IntRange(from = 0) int end, // exclusive, in copied buffer
- /* Maybe Zero */ long nativeBuilderPtr) {
- // Use original text. Shouldn't matter.
- // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for
- // backward compatibility? or Should we initialize them for getFontMetricsInt?
- final float width = replacement.getSize(
- mCachedPaint, mSpanned, start + mTextStart, end + mTextStart, mCachedFm);
- if (nativeBuilderPtr == 0) {
- // Assigns all width to the first character. This is the same behavior as minikin.
- mWidths.set(start, width);
- if (end > start + 1) {
- Arrays.fill(mWidths.getRawArray(), start + 1, end, 0.0f);
- }
- mWholeWidth += width;
- } else {
- nAddReplacementRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
- width);
- }
- }
-
- private void applyStyleRun(@IntRange(from = 0) int start, // inclusive, in copied buffer
- @IntRange(from = 0) int end, // exclusive, in copied buffer
- /* Maybe Zero */ long nativeBuilderPtr) {
- if (nativeBuilderPtr != 0) {
- mCachedPaint.getFontMetricsInt(mCachedFm);
- }
-
- if (mLtrWithoutBidi) {
- // If the whole text is LTR direction, just apply whole region.
- if (nativeBuilderPtr == 0) {
- mWholeWidth += mCachedPaint.getTextRunAdvances(
- mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */,
- mWidths.getRawArray(), start);
- } else {
- nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
- false /* isRtl */);
- }
- } else {
- // If there is multiple bidi levels, split into individual bidi level and apply style.
- byte level = mLevels.get(start);
- // Note that the empty text or empty range won't reach this method.
- // Safe to search from start + 1.
- for (int levelStart = start, levelEnd = start + 1;; ++levelEnd) {
- if (levelEnd == end || mLevels.get(levelEnd) != level) { // transition point
- final boolean isRtl = (level & 0x1) != 0;
- if (nativeBuilderPtr == 0) {
- final int levelLength = levelEnd - levelStart;
- mWholeWidth += mCachedPaint.getTextRunAdvances(
- mCopiedBuffer, levelStart, levelLength, levelStart, levelLength,
- isRtl, mWidths.getRawArray(), levelStart);
- } else {
- nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), levelStart,
- levelEnd, isRtl);
- }
- if (levelEnd == end) {
- break;
- }
- levelStart = levelEnd;
- level = mLevels.get(levelEnd);
- }
- }
- }
- }
-
- private void applyMetricsAffectingSpan(
- @NonNull TextPaint paint,
- @Nullable MetricAffectingSpan[] spans,
- @IntRange(from = 0) int start, // inclusive, in original text buffer
- @IntRange(from = 0) int end, // exclusive, in original text buffer
- /* Maybe Zero */ long nativeBuilderPtr) {
- mCachedPaint.set(paint);
- // XXX paint should not have a baseline shift, but...
- mCachedPaint.baselineShift = 0;
-
- final boolean needFontMetrics = nativeBuilderPtr != 0;
-
- if (needFontMetrics && mCachedFm == null) {
- mCachedFm = new Paint.FontMetricsInt();
- }
-
- ReplacementSpan replacement = null;
- if (spans != null) {
- for (int i = 0; i < spans.length; i++) {
- MetricAffectingSpan span = spans[i];
- if (span instanceof ReplacementSpan) {
- // The last ReplacementSpan is effective for backward compatibility reasons.
- replacement = (ReplacementSpan) span;
- } else {
- // TODO: No need to call updateMeasureState for ReplacementSpan as well?
- span.updateMeasureState(mCachedPaint);
- }
- }
- }
-
- final int startInCopiedBuffer = start - mTextStart;
- final int endInCopiedBuffer = end - mTextStart;
-
- if (replacement != null) {
- applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer,
- nativeBuilderPtr);
- } else {
- applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, nativeBuilderPtr);
- }
-
- if (needFontMetrics) {
- if (mCachedPaint.baselineShift < 0) {
- mCachedFm.ascent += mCachedPaint.baselineShift;
- mCachedFm.top += mCachedPaint.baselineShift;
- } else {
- mCachedFm.descent += mCachedPaint.baselineShift;
- mCachedFm.bottom += mCachedPaint.baselineShift;
- }
-
- mFontMetrics.append(mCachedFm.top);
- mFontMetrics.append(mCachedFm.bottom);
- mFontMetrics.append(mCachedFm.ascent);
- mFontMetrics.append(mCachedFm.descent);
- }
- }
-
- /**
- * Returns the maximum index that the accumulated width not exceeds the width.
- *
- * If forward=false is passed, returns the minimum index from the end instead.
- *
- * This only works if the MeasuredText is computed with computeForMeasurement.
- * Undefined behavior in other case.
- */
- @IntRange(from = 0) int breakText(int limit, boolean forwards, float width) {
- float[] w = mWidths.getRawArray();
- if (forwards) {
- int i = 0;
- while (i < limit) {
- width -= w[i];
- if (width < 0.0f) break;
- i++;
- }
- while (i > 0 && mCopiedBuffer[i - 1] == ' ') i--;
- return i;
- } else {
- int i = limit - 1;
- while (i >= 0) {
- width -= w[i];
- if (width < 0.0f) break;
- i--;
- }
- while (i < limit - 1 && (mCopiedBuffer[i + 1] == ' ' || w[i + 1] == 0.0f)) {
- i++;
- }
- return limit - i - 1;
- }
- }
-
- /**
- * Returns the length of the substring.
- *
- * This only works if the MeasuredText is computed with computeForMeasurement.
- * Undefined behavior in other case.
- */
- @FloatRange(from = 0.0f) float measure(int start, int limit) {
- float width = 0;
- float[] w = mWidths.getRawArray();
- for (int i = start; i < limit; ++i) {
- width += w[i];
- }
- return width;
- }
-
- private static native /* Non Zero */ long nInitBuilder();
-
- /**
- * Apply style to make native measured text.
- *
- * @param nativeBuilderPtr The native MeasuredText builder pointer.
- * @param paintPtr The native paint pointer to be applied.
- * @param start The start offset in the copied buffer.
- * @param end The end offset in the copied buffer.
- * @param isRtl True if the text is RTL.
- */
- private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
- /* Non Zero */ long paintPtr,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- boolean isRtl);
-
- /**
- * Apply ReplacementRun to make native measured text.
- *
- * @param nativeBuilderPtr The native MeasuredText builder pointer.
- * @param paintPtr The native paint pointer to be applied.
- * @param start The start offset in the copied buffer.
- * @param end The end offset in the copied buffer.
- * @param width The width of the replacement.
- */
- private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr,
- /* Non Zero */ long paintPtr,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- @FloatRange(from = 0) float width);
-
- private static native long nBuildNativeMeasuredText(/* Non Zero */ long nativeBuilderPtr,
- @NonNull char[] text);
-
- private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
-
- @CriticalNative
- private static native /* Non Zero */ long nGetReleaseFunc();
}
diff --git a/core/java/android/text/PremeasuredText.java b/core/java/android/text/PremeasuredText.java
deleted file mode 100644
index 465314d..0000000
--- a/core/java/android/text/PremeasuredText.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (C) 2017 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;
-
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.util.IntArray;
-
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.Preconditions;
-
-import java.util.ArrayList;
-
-/**
- * A text which has already been measured.
- *
- * TODO: Rename to better name? e.g. MeasuredText, FrozenText etc.
- */
-public class PremeasuredText implements Spanned {
- private static final char LINE_FEED = '\n';
-
- // The original text.
- private final @NonNull CharSequence mText;
-
- // The inclusive start offset of the measuring target.
- private final @IntRange(from = 0) int mStart;
-
- // The exclusive end offset of the measuring target.
- private final @IntRange(from = 0) int mEnd;
-
- // The TextPaint used for measurement.
- private final @NonNull TextPaint mPaint;
-
- // The requested text direction.
- private final @NonNull TextDirectionHeuristic mTextDir;
-
- // The measured paragraph texts.
- private final @NonNull MeasuredText[] mMeasuredTexts;
-
- // The sorted paragraph end offsets.
- private final @NonNull int[] mParagraphBreakPoints;
-
- /**
- * Build PremeasuredText from the text.
- *
- * @param text The text to be measured.
- * @param paint The paint to be used for drawing.
- * @param textDir The text direction.
- * @return The measured text.
- */
- public static @NonNull PremeasuredText build(@NonNull CharSequence text,
- @NonNull TextPaint paint,
- @NonNull TextDirectionHeuristic textDir) {
- return PremeasuredText.build(text, paint, textDir, 0, text.length());
- }
-
- /**
- * Build PremeasuredText from the specific range of the text..
- *
- * @param text The text to be measured.
- * @param paint The paint to be used for drawing.
- * @param textDir The text direction.
- * @param start The inclusive start offset of the text.
- * @param end The exclusive start offset of the text.
- * @return The measured text.
- */
- public static @NonNull PremeasuredText build(@NonNull CharSequence text,
- @NonNull TextPaint paint,
- @NonNull TextDirectionHeuristic textDir,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end) {
- Preconditions.checkNotNull(text);
- Preconditions.checkNotNull(paint);
- Preconditions.checkNotNull(textDir);
- Preconditions.checkArgumentInRange(start, 0, text.length(), "start");
- Preconditions.checkArgumentInRange(end, 0, text.length(), "end");
-
- final IntArray paragraphEnds = new IntArray();
- final ArrayList<MeasuredText> measuredTexts = new ArrayList<>();
-
- int paraEnd = 0;
- for (int paraStart = start; paraStart < end; paraStart = paraEnd) {
- paraEnd = TextUtils.indexOf(text, LINE_FEED, paraStart, end);
- if (paraEnd < 0) {
- // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph end.
- paraEnd = end;
- } else {
- paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph.
- }
-
- paragraphEnds.add(paraEnd);
- measuredTexts.add(MeasuredText.buildForStaticLayout(
- paint, text, paraStart, paraEnd, textDir, null /* no recycle */));
- }
-
- return new PremeasuredText(text, start, end, paint, textDir,
- measuredTexts.toArray(new MeasuredText[measuredTexts.size()]),
- paragraphEnds.toArray());
- }
-
- // Use PremeasuredText.build instead.
- private PremeasuredText(@NonNull CharSequence text,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- @NonNull TextPaint paint,
- @NonNull TextDirectionHeuristic textDir,
- @NonNull MeasuredText[] measuredTexts,
- @NonNull int[] paragraphBreakPoints) {
- mText = text;
- mStart = start;
- mEnd = end;
- mPaint = paint;
- mMeasuredTexts = measuredTexts;
- mParagraphBreakPoints = paragraphBreakPoints;
- mTextDir = textDir;
- }
-
- /**
- * Return the underlying text.
- */
- public @NonNull CharSequence getText() {
- return mText;
- }
-
- /**
- * Returns the inclusive start offset of measured region.
- */
- public @IntRange(from = 0) int getStart() {
- return mStart;
- }
-
- /**
- * Returns the exclusive end offset of measured region.
- */
- public @IntRange(from = 0) int getEnd() {
- return mEnd;
- }
-
- /**
- * Returns the text direction associated with char sequence.
- */
- public @NonNull TextDirectionHeuristic getTextDir() {
- return mTextDir;
- }
-
- /**
- * Returns the paint used to measure this text.
- */
- public @NonNull TextPaint getPaint() {
- return mPaint;
- }
-
- /**
- * Returns the length of the paragraph of this text.
- */
- public @IntRange(from = 0) int getParagraphCount() {
- return mParagraphBreakPoints.length;
- }
-
- /**
- * Returns the paragraph start offset of the text.
- */
- public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) {
- Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
- return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1];
- }
-
- /**
- * Returns the paragraph end offset of the text.
- */
- public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) {
- Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
- return mParagraphBreakPoints[paraIndex];
- }
-
- /** @hide */
- public @NonNull MeasuredText getMeasuredText(@IntRange(from = 0) int paraIndex) {
- return mMeasuredTexts[paraIndex];
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // Spanned overrides
- //
- // Just proxy for underlying mText if appropriate.
-
- @Override
- public <T> T[] getSpans(int start, int end, Class<T> type) {
- if (mText instanceof Spanned) {
- return ((Spanned) mText).getSpans(start, end, type);
- } else {
- return ArrayUtils.emptyArray(type);
- }
- }
-
- @Override
- public int getSpanStart(Object tag) {
- if (mText instanceof Spanned) {
- return ((Spanned) mText).getSpanStart(tag);
- } else {
- return -1;
- }
- }
-
- @Override
- public int getSpanEnd(Object tag) {
- if (mText instanceof Spanned) {
- return ((Spanned) mText).getSpanEnd(tag);
- } else {
- return -1;
- }
- }
-
- @Override
- public int getSpanFlags(Object tag) {
- if (mText instanceof Spanned) {
- return ((Spanned) mText).getSpanFlags(tag);
- } else {
- return 0;
- }
- }
-
- @Override
- public int nextSpanTransition(int start, int limit, Class type) {
- if (mText instanceof Spanned) {
- return ((Spanned) mText).nextSpanTransition(start, limit, type);
- } else {
- return mText.length();
- }
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // CharSequence overrides.
- //
- // Just proxy for underlying mText.
-
- @Override
- public int length() {
- return mText.length();
- }
-
- @Override
- public char charAt(int index) {
- // TODO: Should this be index + mStart ?
- return mText.charAt(index);
- }
-
- @Override
- public CharSequence subSequence(int start, int end) {
- // TODO: return PremeasuredText.
- // TODO: Should this be index + mStart, end + mStart ?
- return mText.subSequence(start, end);
- }
-
- @Override
- public String toString() {
- return mText.toString();
- }
-}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index d69b119..36bec86 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -55,7 +55,8 @@
* First, call nInit to setup native line breaker object. Then, for each paragraph, do the
* following:
*
- * - Create MeasuredText by MeasuredText.buildForStaticLayout which measures in native.
+ * - Create MeasuredParagraph by MeasuredParagraph.buildForStaticLayout which measures in
+ * native.
* - Run nComputeLineBreaks() to obtain line breaks for the paragraph.
*
* After all paragraphs, call finish() to release expensive buffers.
@@ -650,34 +651,34 @@
b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
indents, mLeftPaddings, mRightPaddings);
- PremeasuredText premeasured = null;
+ MeasuredText measured = null;
final Spanned spanned;
- if (source instanceof PremeasuredText) {
- premeasured = (PremeasuredText) source;
+ if (source instanceof MeasuredText) {
+ measured = (MeasuredText) source;
- final CharSequence original = premeasured.getText();
+ final CharSequence original = measured.getText();
spanned = (original instanceof Spanned) ? (Spanned) original : null;
- if (bufStart != premeasured.getStart() || bufEnd != premeasured.getEnd()) {
+ if (bufStart != measured.getStart() || bufEnd != measured.getEnd()) {
// The buffer position has changed. Re-measure here.
- premeasured = PremeasuredText.build(original, paint, textDir, bufStart, bufEnd);
+ measured = MeasuredText.build(original, paint, textDir, bufStart, bufEnd);
} else {
- // We can use premeasured information.
+ // We can use measured information.
- // Overwrite with the one when premeasured.
+ // Overwrite with the one when emeasured.
// TODO: Give an option for developer not to overwrite and measure again here?
- textDir = premeasured.getTextDir();
- paint = premeasured.getPaint();
+ textDir = measured.getTextDir();
+ paint = measured.getPaint();
}
} else {
- premeasured = PremeasuredText.build(source, paint, textDir, bufStart, bufEnd);
+ measured = MeasuredText.build(source, paint, textDir, bufStart, bufEnd);
spanned = (source instanceof Spanned) ? (Spanned) source : null;
}
try {
- for (int paraIndex = 0; paraIndex < premeasured.getParagraphCount(); paraIndex++) {
- final int paraStart = premeasured.getParagraphStart(paraIndex);
- final int paraEnd = premeasured.getParagraphEnd(paraIndex);
+ for (int paraIndex = 0; paraIndex < measured.getParagraphCount(); paraIndex++) {
+ final int paraStart = measured.getParagraphStart(paraIndex);
+ final int paraEnd = measured.getParagraphEnd(paraIndex);
int firstWidthLineCount = 1;
int firstWidth = outerWidth;
@@ -743,10 +744,10 @@
}
}
- final MeasuredText measured = premeasured.getMeasuredText(paraIndex);
- final char[] chs = measured.getChars();
- final int[] spanEndCache = measured.getSpanEndCache().getRawArray();
- final int[] fmCache = measured.getFontMetrics().getRawArray();
+ final MeasuredParagraph measuredPara = measured.getMeasuredParagraph(paraIndex);
+ final char[] chs = measuredPara.getChars();
+ final int[] spanEndCache = measuredPara.getSpanEndCache().getRawArray();
+ final int[] fmCache = measuredPara.getFontMetrics().getRawArray();
// TODO: Stop keeping duplicated width copy in native and Java.
widths.resize(chs.length);
@@ -759,7 +760,7 @@
// Inputs
chs,
- measured.getNativePtr(),
+ measuredPara.getNativePtr(),
paraEnd - paraStart,
firstWidth,
firstWidthLineCount,
@@ -863,7 +864,7 @@
v = out(source, here, endPos,
ascent, descent, fmTop, fmBottom,
v, spacingmult, spacingadd, chooseHt, chooseHtv, fm,
- flags[breakIndex], needMultiply, measured, bufEnd,
+ flags[breakIndex], needMultiply, measuredPara, bufEnd,
includepad, trackpad, addLastLineSpacing, chs, widths.getRawArray(),
paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex],
paint, moreChars);
@@ -894,8 +895,8 @@
if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE)
&& mLineCount < mMaximumVisibleLineCount) {
- final MeasuredText measured =
- MeasuredText.buildForBidi(source, bufEnd, bufEnd, textDir, null);
+ final MeasuredParagraph measuredPara =
+ MeasuredParagraph.buildForBidi(source, bufEnd, bufEnd, textDir, null);
paint.getFontMetricsInt(fm);
v = out(source,
bufEnd, bufEnd, fm.ascent, fm.descent,
@@ -903,7 +904,7 @@
v,
spacingmult, spacingadd, null,
null, fm, 0,
- needMultiply, measured, bufEnd,
+ needMultiply, measuredPara, bufEnd,
includepad, trackpad, addLastLineSpacing, null,
null, bufStart, ellipsize,
ellipsizedWidth, 0, paint, false);
@@ -918,7 +919,7 @@
private int out(final CharSequence text, final int start, final int end, int above, int below,
int top, int bottom, int v, final float spacingmult, final float spacingadd,
final LineHeightSpan[] chooseHt, final int[] chooseHtv, final Paint.FontMetricsInt fm,
- final int flags, final boolean needMultiply, @NonNull final MeasuredText measured,
+ final int flags, final boolean needMultiply, @NonNull final MeasuredParagraph measured,
final int bufEnd, final boolean includePad, final boolean trackPad,
final boolean addLastLineLineSpacing, final char[] chs, final float[] widths,
final int widthStart, final TextUtils.TruncateAt ellipsize, final float ellipsisWidth,
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 9c9fbf2..409e514 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -1250,10 +1250,10 @@
@NonNull String ellipsis) {
final int len = text.length();
- MeasuredText mt = null;
- MeasuredText resultMt = null;
+ MeasuredParagraph mt = null;
+ MeasuredParagraph resultMt = null;
try {
- mt = MeasuredText.buildForMeasurement(paint, text, 0, text.length(), textDir, mt);
+ mt = MeasuredParagraph.buildForMeasurement(paint, text, 0, text.length(), textDir, mt);
float width = mt.getWholeWidth();
if (width <= avail) {
@@ -1332,7 +1332,7 @@
if (remaining == 0) { // All text is gone.
textFits = true;
} else {
- resultMt = MeasuredText.buildForMeasurement(
+ resultMt = MeasuredParagraph.buildForMeasurement(
paint, result, 0, result.length(), textDir, resultMt);
width = resultMt.getWholeWidth();
if (width <= avail) {
@@ -1479,11 +1479,11 @@
public static CharSequence commaEllipsize(CharSequence text, TextPaint p,
float avail, String oneMore, String more, TextDirectionHeuristic textDir) {
- MeasuredText mt = null;
- MeasuredText tempMt = null;
+ MeasuredParagraph mt = null;
+ MeasuredParagraph tempMt = null;
try {
int len = text.length();
- mt = MeasuredText.buildForMeasurement(p, text, 0, len, textDir, mt);
+ mt = MeasuredParagraph.buildForMeasurement(p, text, 0, len, textDir, mt);
final float width = mt.getWholeWidth();
if (width <= avail) {
return text;
@@ -1523,7 +1523,7 @@
}
// XXX this is probably ok, but need to look at it more
- tempMt = MeasuredText.buildForMeasurement(
+ tempMt = MeasuredParagraph.buildForMeasurement(
p, format, 0, format.length(), textDir, tempMt);
float moreWid = tempMt.getWholeWidth();
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index bbd9c9c..562ae7a 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -358,7 +358,7 @@
}
/**
- * Return the current time in YYYYMMDDTHHMMSS<tz> format
+ * Return the current time in YYYYMMDDTHHMMSS<tz> format
*/
@Override
public String toString() {
@@ -738,6 +738,7 @@
* <p>
* You should also use <tt>toMillis(false)</tt> if you want
* to read back the same milliseconds that you set with {@link #set(long)}
+ * or {@link #set(Time)} or after parsing a date string.
*
* <p>
* This method can return {@code -1} when the date / time fields have been
@@ -745,8 +746,6 @@
* For example, when daylight savings transitions cause an hour to be
* skipped: times within that hour will return {@code -1} if isDst =
* {@code -1}.
- *
- * or {@link #set(Time)} or after parsing a date string.
*/
public long toMillis(boolean ignoreDst) {
calculator.copyFieldsFromTime(this);
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 1d5392e..e94f91a 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -38,12 +38,13 @@
static {
DEFAULT_FLAGS = new HashMap<>();
DEFAULT_FLAGS.put("device_info_v2", "true");
- DEFAULT_FLAGS.put("settings_search_v2", "true");
- DEFAULT_FLAGS.put("settings_app_info_v2", "false");
+ DEFAULT_FLAGS.put("settings_app_info_v2", "true");
DEFAULT_FLAGS.put("settings_connected_device_v2", "true");
DEFAULT_FLAGS.put("settings_battery_v2", "false");
DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
DEFAULT_FLAGS.put("settings_security_settings_v2", "true");
+ DEFAULT_FLAGS.put("settings_zone_picker_v2", "false");
+ DEFAULT_FLAGS.put("settings_suggestion_ui_v2", "false");
}
/**
diff --git a/core/java/android/util/StatsManager.java b/core/java/android/util/StatsManager.java
index c25b272..e0d085c 100644
--- a/core/java/android/util/StatsManager.java
+++ b/core/java/android/util/StatsManager.java
@@ -42,6 +42,16 @@
}
/**
+ * Temporary to prevent build failures. Will be deleted.
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public boolean addConfiguration(String configKey, byte[] config, String pkg, String cls) {
+ // To prevent breakages of dependencies on old API.
+
+ return false;
+ }
+
+ /**
* Clients can send a configuration and simultaneously registers the name of a broadcast
* receiver that listens for when it should request data.
*
@@ -70,6 +80,15 @@
}
/**
+ * Temporary to prevent build failures. Will be deleted.
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public boolean removeConfiguration(String configKey) {
+ // To prevent breakages of old dependencies.
+ return false;
+ }
+
+ /**
* Remove a configuration from logging.
*
* @param configKey Configuration key to remove.
@@ -93,6 +112,16 @@
}
/**
+ * Temporary to prevent build failures. Will be deleted.
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public byte[] getData(String configKey) {
+ // TODO: remove this and all other methods with String-based config keys.
+ // To prevent build breakages of dependencies.
+ return null;
+ }
+
+ /**
* Clients can request data with a binder call. This getter is destructive and also clears
* the retrieved metrics from statsd memory.
*
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index cc4a0b6..84ae20b 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -18,30 +18,18 @@
import android.os.SystemClock;
-import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
import libcore.util.TimeZoneFinder;
import libcore.util.ZoneInfoDB;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
/**
* A class containing utility methods related to time zones.
*/
public class TimeUtils {
/** @hide */ public TimeUtils() {}
- private static final boolean DBG = false;
- private static final String TAG = "TimeUtils";
-
- /** Cached results of getTimeZonesWithUniqueOffsets */
- private static final Object sLastUniqueLockObj = new Object();
- private static List<String> sLastUniqueZoneOffsets = null;
- private static String sLastUniqueCountry = null;
-
/** {@hide} */
private static SimpleDateFormat sLoggingFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@@ -76,86 +64,6 @@
}
/**
- * Returns an immutable list of unique time zone IDs for the country.
- *
- * @param country to find
- * @return unmodifiable list of unique time zones, maybe empty but never null.
- * @hide
- */
- public static List<String> getTimeZoneIdsWithUniqueOffsets(String country) {
- synchronized(sLastUniqueLockObj) {
- if ((country != null) && country.equals(sLastUniqueCountry)) {
- if (DBG) {
- Log.d(TAG, "getTimeZonesWithUniqueOffsets(" +
- country + "): return cached version");
- }
- return sLastUniqueZoneOffsets;
- }
- }
-
- Collection<android.icu.util.TimeZone> zones = getIcuTimeZones(country);
- ArrayList<android.icu.util.TimeZone> uniqueTimeZones = new ArrayList<>();
- for (android.icu.util.TimeZone zone : zones) {
- // See if we already have this offset,
- // Using slow but space efficient and these are small.
- boolean found = false;
- for (int i = 0; i < uniqueTimeZones.size(); i++) {
- if (uniqueTimeZones.get(i).getRawOffset() == zone.getRawOffset()) {
- found = true;
- break;
- }
- }
- if (!found) {
- if (DBG) {
- Log.d(TAG, "getTimeZonesWithUniqueOffsets: add unique offset=" +
- zone.getRawOffset() + " zone.getID=" + zone.getID());
- }
- uniqueTimeZones.add(zone);
- }
- }
-
- synchronized(sLastUniqueLockObj) {
- // Cache the last result
- sLastUniqueZoneOffsets = extractZoneIds(uniqueTimeZones);
- sLastUniqueCountry = country;
-
- return sLastUniqueZoneOffsets;
- }
- }
-
- private static List<String> extractZoneIds(List<android.icu.util.TimeZone> timeZones) {
- List<String> ids = new ArrayList<>(timeZones.size());
- for (android.icu.util.TimeZone timeZone : timeZones) {
- ids.add(timeZone.getID());
- }
- return Collections.unmodifiableList(ids);
- }
-
- /**
- * Returns an immutable list of frozen ICU time zones for the country.
- *
- * @param countryIso is a two character country code.
- * @return TimeZone list, maybe empty but never null.
- * @hide
- */
- private static List<android.icu.util.TimeZone> getIcuTimeZones(String countryIso) {
- if (countryIso == null) {
- if (DBG) Log.d(TAG, "getIcuTimeZones(null): return empty list");
- return Collections.emptyList();
- }
- List<android.icu.util.TimeZone> timeZones =
- TimeZoneFinder.getInstance().lookupTimeZonesByCountry(countryIso);
- if (timeZones == null) {
- if (DBG) {
- Log.d(TAG, "getIcuTimeZones(" + countryIso
- + "): returned null, converting to empty list");
- }
- return Collections.emptyList();
- }
- return timeZones;
- }
-
- /**
* Returns a String indicating the version of the time zone database currently
* in use. The format of the string is dependent on the underlying time zone
* database implementation, but will typically contain the year in which the database
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 530937e..ce8998f 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -23,6 +23,9 @@
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PSS_WITH_SHA256;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PSS_WITH_SHA512;
+import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_DSA_WITH_SHA256;
+import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_ECDSA_WITH_SHA256;
+import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256;
import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getLengthPrefixedSlice;
@@ -35,7 +38,6 @@
import android.util.Pair;
import java.io.ByteArrayInputStream;
-import java.io.FileDescriptor;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.BufferUnderflowException;
@@ -139,7 +141,7 @@
private static X509Certificate[][] verify(RandomAccessFile apk, boolean verifyIntegrity)
throws SignatureNotFoundException, SecurityException, IOException {
SignatureInfo signatureInfo = findSignature(apk);
- return verify(apk.getFD(), signatureInfo, verifyIntegrity);
+ return verify(apk, signatureInfo, verifyIntegrity);
}
/**
@@ -162,9 +164,9 @@
* against the APK file.
*/
private static X509Certificate[][] verify(
- FileDescriptor apkFileDescriptor,
+ RandomAccessFile apk,
SignatureInfo signatureInfo,
- boolean doVerifyIntegrity) throws SecurityException {
+ boolean doVerifyIntegrity) throws SecurityException, IOException {
int signerCount = 0;
Map<Integer, byte[]> contentDigests = new ArrayMap<>();
List<X509Certificate[]> signerCerts = new ArrayList<>();
@@ -202,13 +204,7 @@
}
if (doVerifyIntegrity) {
- ApkSigningBlockUtils.verifyIntegrity(
- contentDigests,
- apkFileDescriptor,
- signatureInfo.apkSigningBlockOffset,
- signatureInfo.centralDirOffset,
- signatureInfo.eocdOffset,
- signatureInfo.eocd);
+ ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo);
}
return signerCerts.toArray(new X509Certificate[signerCerts.size()][]);
@@ -386,6 +382,7 @@
}
return;
}
+
private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) {
switch (sigAlgorithm) {
case SIGNATURE_RSA_PSS_WITH_SHA256:
@@ -395,6 +392,9 @@
case SIGNATURE_ECDSA_WITH_SHA256:
case SIGNATURE_ECDSA_WITH_SHA512:
case SIGNATURE_DSA_WITH_SHA256:
+ case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256:
+ case SIGNATURE_VERITY_ECDSA_WITH_SHA256:
+ case SIGNATURE_VERITY_DSA_WITH_SHA256:
return true;
default:
return false;
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index e43dee3..c9e67fe1 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -23,6 +23,9 @@
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PSS_WITH_SHA256;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PSS_WITH_SHA512;
+import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_DSA_WITH_SHA256;
+import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_ECDSA_WITH_SHA256;
+import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256;
import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getLengthPrefixedSlice;
@@ -36,7 +39,6 @@
import android.util.Pair;
import java.io.ByteArrayInputStream;
-import java.io.FileDescriptor;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.BufferUnderflowException;
@@ -136,7 +138,7 @@
private static VerifiedSigner verify(RandomAccessFile apk, boolean verifyIntegrity)
throws SignatureNotFoundException, SecurityException, IOException {
SignatureInfo signatureInfo = findSignature(apk);
- return verify(apk.getFD(), signatureInfo, verifyIntegrity);
+ return verify(apk, signatureInfo, verifyIntegrity);
}
/**
@@ -159,7 +161,7 @@
* against the APK file.
*/
private static VerifiedSigner verify(
- FileDescriptor apkFileDescriptor,
+ RandomAccessFile apk,
SignatureInfo signatureInfo,
boolean doVerifyIntegrity) throws SecurityException {
int signerCount = 0;
@@ -206,13 +208,7 @@
}
if (doVerifyIntegrity) {
- ApkSigningBlockUtils.verifyIntegrity(
- contentDigests,
- apkFileDescriptor,
- signatureInfo.apkSigningBlockOffset,
- signatureInfo.centralDirOffset,
- signatureInfo.eocdOffset,
- signatureInfo.eocd);
+ ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo);
}
return result;
@@ -512,6 +508,9 @@
case SIGNATURE_ECDSA_WITH_SHA256:
case SIGNATURE_ECDSA_WITH_SHA512:
case SIGNATURE_DSA_WITH_SHA256:
+ case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256:
+ case SIGNATURE_VERITY_ECDSA_WITH_SHA256:
+ case SIGNATURE_VERITY_DSA_WITH_SHA256:
return true;
default:
return false;
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index 8146729..555c474 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -25,6 +25,7 @@
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.Signature;
import android.os.Trace;
import android.util.jar.StrictJarFile;
@@ -52,10 +53,6 @@
*/
public class ApkSignatureVerifier {
- public static final int VERSION_JAR_SIGNATURE_SCHEME = 1;
- public static final int VERSION_APK_SIGNATURE_SCHEME_V2 = 2;
- public static final int VERSION_APK_SIGNATURE_SCHEME_V3 = 3;
-
private static final AtomicReference<byte[]> sBuffer = new AtomicReference<>();
/**
@@ -63,10 +60,11 @@
*
* @throws PackageParserException if the APK's signature failed to verify.
*/
- public static Result verify(String apkPath, int minSignatureSchemeVersion)
+ public static PackageParser.SigningDetails verify(String apkPath,
+ @SignatureSchemeVersion int minSignatureSchemeVersion)
throws PackageParserException {
- if (minSignatureSchemeVersion > VERSION_APK_SIGNATURE_SCHEME_V3) {
+ if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) {
// V3 and before are older than the requested minimum signing version
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No signature found in package of version " + minSignatureSchemeVersion
@@ -80,10 +78,11 @@
ApkSignatureSchemeV3Verifier.verify(apkPath);
Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
Signature[] signerSigs = convertToSignatures(signerCerts);
- return new Result(signerCerts, signerSigs, VERSION_APK_SIGNATURE_SCHEME_V3);
+ return new PackageParser.SigningDetails(signerSigs,
+ SignatureSchemeVersion.SIGNING_BLOCK_V3);
} catch (SignatureNotFoundException e) {
// not signed with v2, try older if allowed
- if (minSignatureSchemeVersion >= VERSION_APK_SIGNATURE_SCHEME_V3) {
+ if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No APK Signature Scheme v3 signature in package " + apkPath, e);
}
@@ -97,7 +96,7 @@
}
// redundant, protective version check
- if (minSignatureSchemeVersion > VERSION_APK_SIGNATURE_SCHEME_V2) {
+ if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V2) {
// V2 and before are older than the requested minimum signing version
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No signature found in package of version " + minSignatureSchemeVersion
@@ -110,10 +109,11 @@
Certificate[][] signerCerts = ApkSignatureSchemeV2Verifier.verify(apkPath);
Signature[] signerSigs = convertToSignatures(signerCerts);
- return new Result(signerCerts, signerSigs, VERSION_APK_SIGNATURE_SCHEME_V2);
+ return new PackageParser.SigningDetails(
+ signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V2);
} catch (SignatureNotFoundException e) {
// not signed with v2, try older if allowed
- if (minSignatureSchemeVersion >= VERSION_APK_SIGNATURE_SCHEME_V2) {
+ if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V2) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No APK Signature Scheme v2 signature in package " + apkPath, e);
}
@@ -127,7 +127,7 @@
}
// redundant, protective version check
- if (minSignatureSchemeVersion > VERSION_JAR_SIGNATURE_SCHEME) {
+ if (minSignatureSchemeVersion > SignatureSchemeVersion.JAR) {
// V1 and is older than the requested minimum signing version
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No signature found in package of version " + minSignatureSchemeVersion
@@ -145,7 +145,8 @@
*
* @throws PackageParserException if there was a problem collecting certificates
*/
- private static Result verifyV1Signature(String apkPath, boolean verifyFull)
+ private static PackageParser.SigningDetails verifyV1Signature(
+ String apkPath, boolean verifyFull)
throws PackageParserException {
StrictJarFile jarFile = null;
@@ -211,7 +212,7 @@
}
}
}
- return new Result(lastCerts, lastSigs, VERSION_JAR_SIGNATURE_SCHEME);
+ return new PackageParser.SigningDetails(lastSigs, SignatureSchemeVersion.JAR);
} catch (GeneralSecurityException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
"Failed to collect certificates from " + apkPath, e);
@@ -289,10 +290,11 @@
* @throws PackageParserException if the APK's signature failed to verify.
* or greater is not found, except in the case of no JAR signature.
*/
- public static Result plsCertsNoVerifyOnlyCerts(String apkPath, int minSignatureSchemeVersion)
+ public static PackageParser.SigningDetails plsCertsNoVerifyOnlyCerts(
+ String apkPath, int minSignatureSchemeVersion)
throws PackageParserException {
- if (minSignatureSchemeVersion > VERSION_APK_SIGNATURE_SCHEME_V3) {
+ if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) {
// V3 and before are older than the requested minimum signing version
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No signature found in package of version " + minSignatureSchemeVersion
@@ -306,10 +308,11 @@
ApkSignatureSchemeV3Verifier.plsCertsNoVerifyOnlyCerts(apkPath);
Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
Signature[] signerSigs = convertToSignatures(signerCerts);
- return new Result(signerCerts, signerSigs, VERSION_APK_SIGNATURE_SCHEME_V3);
+ return new PackageParser.SigningDetails(signerSigs,
+ SignatureSchemeVersion.SIGNING_BLOCK_V3);
} catch (SignatureNotFoundException e) {
// not signed with v2, try older if allowed
- if (minSignatureSchemeVersion >= VERSION_APK_SIGNATURE_SCHEME_V3) {
+ if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No APK Signature Scheme v3 signature in package " + apkPath, e);
}
@@ -323,7 +326,7 @@
}
// redundant, protective version check
- if (minSignatureSchemeVersion > VERSION_APK_SIGNATURE_SCHEME_V2) {
+ if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V2) {
// V2 and before are older than the requested minimum signing version
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No signature found in package of version " + minSignatureSchemeVersion
@@ -336,10 +339,11 @@
Certificate[][] signerCerts =
ApkSignatureSchemeV2Verifier.plsCertsNoVerifyOnlyCerts(apkPath);
Signature[] signerSigs = convertToSignatures(signerCerts);
- return new Result(signerCerts, signerSigs, VERSION_APK_SIGNATURE_SCHEME_V2);
+ return new PackageParser.SigningDetails(signerSigs,
+ SignatureSchemeVersion.SIGNING_BLOCK_V2);
} catch (SignatureNotFoundException e) {
// not signed with v2, try older if allowed
- if (minSignatureSchemeVersion >= VERSION_APK_SIGNATURE_SCHEME_V2) {
+ if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V2) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No APK Signature Scheme v2 signature in package " + apkPath, e);
}
@@ -353,7 +357,7 @@
}
// redundant, protective version check
- if (minSignatureSchemeVersion > VERSION_JAR_SIGNATURE_SCHEME) {
+ if (minSignatureSchemeVersion > SignatureSchemeVersion.JAR) {
// V1 and is older than the requested minimum signing version
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No signature found in package of version " + minSignatureSchemeVersion
@@ -363,19 +367,4 @@
// v2 didn't work, try jarsigner
return verifyV1Signature(apkPath, false);
}
-
- /**
- * Result of a successful APK verification operation.
- */
- public static class Result {
- public final Certificate[][] certs;
- public final Signature[] sigs;
- public final int signatureSchemeVersion;
-
- public Result(Certificate[][] certs, Signature[] sigs, int signingVersion) {
- this.certs = certs;
- this.sigs = sigs;
- this.signatureSchemeVersion = signingVersion;
- }
- }
}
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index 9279510..9d53847 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -16,6 +16,7 @@
package android.util.apk;
+import android.util.ArrayMap;
import android.util.Pair;
import java.io.FileDescriptor;
@@ -30,6 +31,7 @@
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PSSParameterSpec;
+import java.util.Arrays;
import java.util.Map;
/**
@@ -84,16 +86,41 @@
static void verifyIntegrity(
Map<Integer, byte[]> expectedDigests,
- FileDescriptor apkFileDescriptor,
- long apkSigningBlockOffset,
- long centralDirOffset,
- long eocdOffset,
- ByteBuffer eocdBuf) throws SecurityException {
-
+ RandomAccessFile apk,
+ SignatureInfo signatureInfo) throws SecurityException {
if (expectedDigests.isEmpty()) {
throw new SecurityException("No digests provided");
}
+ Map<Integer, byte[]> expected1MbChunkDigests = new ArrayMap<>();
+ if (expectedDigests.containsKey(CONTENT_DIGEST_CHUNKED_SHA256)) {
+ expected1MbChunkDigests.put(CONTENT_DIGEST_CHUNKED_SHA256,
+ expectedDigests.get(CONTENT_DIGEST_CHUNKED_SHA256));
+ }
+ if (expectedDigests.containsKey(CONTENT_DIGEST_CHUNKED_SHA512)) {
+ expected1MbChunkDigests.put(CONTENT_DIGEST_CHUNKED_SHA512,
+ expectedDigests.get(CONTENT_DIGEST_CHUNKED_SHA512));
+ }
+
+ if (expectedDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
+ verifyIntegrityForVerityBasedAlgorithm(
+ expectedDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256), apk, signatureInfo);
+ } else if (!expected1MbChunkDigests.isEmpty()) {
+ try {
+ verifyIntegrityFor1MbChunkBasedAlgorithm(expected1MbChunkDigests, apk.getFD(),
+ signatureInfo);
+ } catch (IOException e) {
+ throw new SecurityException("Cannot get FD", e);
+ }
+ } else {
+ throw new SecurityException("No known digest exists for integrity check");
+ }
+ }
+
+ private static void verifyIntegrityFor1MbChunkBasedAlgorithm(
+ Map<Integer, byte[]> expectedDigests,
+ FileDescriptor apkFileDescriptor,
+ SignatureInfo signatureInfo) throws SecurityException {
// We need to verify the integrity of the following three sections of the file:
// 1. Everything up to the start of the APK Signing Block.
// 2. ZIP Central Directory.
@@ -105,16 +132,18 @@
// APK are already there in the OS's page cache and thus mmap does not use additional
// physical memory.
DataSource beforeApkSigningBlock =
- new MemoryMappedFileDataSource(apkFileDescriptor, 0, apkSigningBlockOffset);
+ new MemoryMappedFileDataSource(apkFileDescriptor, 0,
+ signatureInfo.apkSigningBlockOffset);
DataSource centralDir =
new MemoryMappedFileDataSource(
- apkFileDescriptor, centralDirOffset, eocdOffset - centralDirOffset);
+ apkFileDescriptor, signatureInfo.centralDirOffset,
+ signatureInfo.eocdOffset - signatureInfo.centralDirOffset);
// For the purposes of integrity verification, ZIP End of Central Directory's field Start of
// Central Directory must be considered to point to the offset of the APK Signing Block.
- eocdBuf = eocdBuf.duplicate();
+ ByteBuffer eocdBuf = signatureInfo.eocd.duplicate();
eocdBuf.order(ByteOrder.LITTLE_ENDIAN);
- ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, apkSigningBlockOffset);
+ ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, signatureInfo.apkSigningBlockOffset);
DataSource eocd = new ByteBufferDataSource(eocdBuf);
int[] digestAlgorithms = new int[expectedDigests.size()];
@@ -126,7 +155,7 @@
byte[][] actualDigests;
try {
actualDigests =
- computeContentDigests(
+ computeContentDigestsPer1MbChunk(
digestAlgorithms,
new DataSource[] {beforeApkSigningBlock, centralDir, eocd});
} catch (DigestException e) {
@@ -144,7 +173,7 @@
}
}
- private static byte[][] computeContentDigests(
+ private static byte[][] computeContentDigestsPer1MbChunk(
int[] digestAlgorithms,
DataSource[] contents) throws DigestException {
// For each digest algorithm the result is computed as follows:
@@ -256,6 +285,26 @@
return result;
}
+ private static void verifyIntegrityForVerityBasedAlgorithm(
+ byte[] expectedRootHash,
+ RandomAccessFile apk,
+ SignatureInfo signatureInfo) throws SecurityException {
+ try {
+ ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerity(apk,
+ signatureInfo, new ByteBufferFactory() {
+ @Override
+ public ByteBuffer create(int capacity) {
+ return ByteBuffer.allocate(capacity);
+ }
+ });
+ if (!Arrays.equals(expectedRootHash, verity.rootHash)) {
+ throw new SecurityException("APK verity digest of contents did not verify");
+ }
+ } catch (DigestException | IOException | NoSuchAlgorithmException e) {
+ throw new SecurityException("Error during verification", e);
+ }
+ }
+
/**
* Returns the ZIP End of Central Directory (EoCD) and its offset in the file.
*
@@ -304,9 +353,13 @@
static final int SIGNATURE_ECDSA_WITH_SHA256 = 0x0201;
static final int SIGNATURE_ECDSA_WITH_SHA512 = 0x0202;
static final int SIGNATURE_DSA_WITH_SHA256 = 0x0301;
+ static final int SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256 = 0x0401;
+ static final int SIGNATURE_VERITY_ECDSA_WITH_SHA256 = 0x0403;
+ static final int SIGNATURE_VERITY_DSA_WITH_SHA256 = 0x0405;
static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1;
static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2;
+ static final int CONTENT_DIGEST_VERITY_CHUNKED_SHA256 = 3;
static int compareSignatureAlgorithm(int sigAlgorithm1, int sigAlgorithm2) {
int digestAlgorithm1 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm1);
@@ -321,6 +374,7 @@
case CONTENT_DIGEST_CHUNKED_SHA256:
return 0;
case CONTENT_DIGEST_CHUNKED_SHA512:
+ case CONTENT_DIGEST_VERITY_CHUNKED_SHA256:
return -1;
default:
throw new IllegalArgumentException(
@@ -329,6 +383,7 @@
case CONTENT_DIGEST_CHUNKED_SHA512:
switch (digestAlgorithm2) {
case CONTENT_DIGEST_CHUNKED_SHA256:
+ case CONTENT_DIGEST_VERITY_CHUNKED_SHA256:
return 1;
case CONTENT_DIGEST_CHUNKED_SHA512:
return 0;
@@ -336,6 +391,18 @@
throw new IllegalArgumentException(
"Unknown digestAlgorithm2: " + digestAlgorithm2);
}
+ case CONTENT_DIGEST_VERITY_CHUNKED_SHA256:
+ switch (digestAlgorithm2) {
+ case CONTENT_DIGEST_CHUNKED_SHA512:
+ return -1;
+ case CONTENT_DIGEST_VERITY_CHUNKED_SHA256:
+ return 0;
+ case CONTENT_DIGEST_CHUNKED_SHA256:
+ return 1;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown digestAlgorithm2: " + digestAlgorithm2);
+ }
default:
throw new IllegalArgumentException("Unknown digestAlgorithm1: " + digestAlgorithm1);
}
@@ -352,6 +419,10 @@
case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512:
case SIGNATURE_ECDSA_WITH_SHA512:
return CONTENT_DIGEST_CHUNKED_SHA512;
+ case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256:
+ case SIGNATURE_VERITY_ECDSA_WITH_SHA256:
+ case SIGNATURE_VERITY_DSA_WITH_SHA256:
+ return CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
default:
throw new IllegalArgumentException(
"Unknown signature algorithm: 0x"
@@ -362,6 +433,7 @@
static String getContentDigestAlgorithmJcaDigestAlgorithm(int digestAlgorithm) {
switch (digestAlgorithm) {
case CONTENT_DIGEST_CHUNKED_SHA256:
+ case CONTENT_DIGEST_VERITY_CHUNKED_SHA256:
return "SHA-256";
case CONTENT_DIGEST_CHUNKED_SHA512:
return "SHA-512";
@@ -374,6 +446,7 @@
private static int getContentDigestAlgorithmOutputSizeBytes(int digestAlgorithm) {
switch (digestAlgorithm) {
case CONTENT_DIGEST_CHUNKED_SHA256:
+ case CONTENT_DIGEST_VERITY_CHUNKED_SHA256:
return 256 / 8;
case CONTENT_DIGEST_CHUNKED_SHA512:
return 512 / 8;
@@ -389,11 +462,14 @@
case SIGNATURE_RSA_PSS_WITH_SHA512:
case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256:
case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512:
+ case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256:
return "RSA";
case SIGNATURE_ECDSA_WITH_SHA256:
case SIGNATURE_ECDSA_WITH_SHA512:
+ case SIGNATURE_VERITY_ECDSA_WITH_SHA256:
return "EC";
case SIGNATURE_DSA_WITH_SHA256:
+ case SIGNATURE_VERITY_DSA_WITH_SHA256:
return "DSA";
default:
throw new IllegalArgumentException(
@@ -416,14 +492,17 @@
new PSSParameterSpec(
"SHA-512", "MGF1", MGF1ParameterSpec.SHA512, 512 / 8, 1));
case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256:
+ case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256:
return Pair.create("SHA256withRSA", null);
case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512:
return Pair.create("SHA512withRSA", null);
case SIGNATURE_ECDSA_WITH_SHA256:
+ case SIGNATURE_VERITY_ECDSA_WITH_SHA256:
return Pair.create("SHA256withECDSA", null);
case SIGNATURE_ECDSA_WITH_SHA512:
return Pair.create("SHA512withECDSA", null);
case SIGNATURE_DSA_WITH_SHA256:
+ case SIGNATURE_VERITY_DSA_WITH_SHA256:
return Pair.create("SHA256withDSA", null);
default:
throw new IllegalArgumentException(
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 3dcfd00..1ef5f09 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -153,7 +153,7 @@
@Override
public String toString() {
return "DisplayCutout{insets=" + mSafeInsets
- + " bounds=" + mBounds
+ + " boundingRect=" + getBoundingRect()
+ "}";
}
@@ -279,9 +279,7 @@
* @hide
*/
public static DisplayCutout fromBoundingPolygon(List<Point> points) {
- Region bounds = Region.obtain();
Path path = new Path();
-
path.reset();
for (int i = 0; i < points.size(); i++) {
Point point = points.get(i);
@@ -292,14 +290,24 @@
}
}
path.close();
+ return fromBounds(path);
+ }
+ /**
+ * Creates an instance from a bounding {@link Path}.
+ *
+ * @hide
+ */
+ public static DisplayCutout fromBounds(Path path) {
RectF clipRect = new RectF();
path.computeBounds(clipRect, false /* unused */);
Region clipRegion = Region.obtain();
clipRegion.set((int) clipRect.left, (int) clipRect.top,
(int) clipRect.right, (int) clipRect.bottom);
+ Region bounds = new Region();
bounds.setPath(path, clipRegion);
+ clipRegion.recycle();
return new DisplayCutout(ZERO_RECT, bounds);
}
@@ -329,12 +337,23 @@
@Override
public void writeToParcel(Parcel out, int flags) {
- if (mInner == NO_CUTOUT) {
+ writeCutoutToParcel(mInner, out, flags);
+ }
+
+ /**
+ * Writes a DisplayCutout to a {@link Parcel}.
+ *
+ * @see #readCutoutFromParcel(Parcel)
+ */
+ public static void writeCutoutToParcel(DisplayCutout cutout, Parcel out, int flags) {
+ if (cutout == null) {
+ out.writeInt(-1);
+ } else if (cutout == NO_CUTOUT) {
out.writeInt(0);
} else {
out.writeInt(1);
- out.writeTypedObject(mInner.mSafeInsets, flags);
- out.writeTypedObject(mInner.mBounds, flags);
+ out.writeTypedObject(cutout.mSafeInsets, flags);
+ out.writeTypedObject(cutout.mBounds, flags);
}
}
@@ -345,13 +364,13 @@
* Needed for AIDL out parameters.
*/
public void readFromParcel(Parcel in) {
- mInner = readCutout(in);
+ mInner = readCutoutFromParcel(in);
}
public static final Creator<ParcelableWrapper> CREATOR = new Creator<ParcelableWrapper>() {
@Override
public ParcelableWrapper createFromParcel(Parcel in) {
- return new ParcelableWrapper(readCutout(in));
+ return new ParcelableWrapper(readCutoutFromParcel(in));
}
@Override
@@ -360,8 +379,17 @@
}
};
- private static DisplayCutout readCutout(Parcel in) {
- if (in.readInt() == 0) {
+ /**
+ * Reads a DisplayCutout from a {@link Parcel}.
+ *
+ * @see #writeCutoutToParcel(DisplayCutout, Parcel, int)
+ */
+ public static DisplayCutout readCutoutFromParcel(Parcel in) {
+ int variant = in.readInt();
+ if (variant == -1) {
+ return null;
+ }
+ if (variant == 0) {
return NO_CUTOUT;
}
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index b813ddb..37e9815 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -149,6 +149,13 @@
public int overscanBottom;
/**
+ * The {@link DisplayCutout} if present, otherwise {@code null}.
+ *
+ * @hide
+ */
+ public DisplayCutout displayCutout;
+
+ /**
* The rotation of the display relative to its natural orientation.
* May be one of {@link android.view.Surface#ROTATION_0},
* {@link android.view.Surface#ROTATION_90}, {@link android.view.Surface#ROTATION_180},
@@ -301,6 +308,7 @@
&& overscanTop == other.overscanTop
&& overscanRight == other.overscanRight
&& overscanBottom == other.overscanBottom
+ && Objects.equal(displayCutout, other.displayCutout)
&& rotation == other.rotation
&& modeId == other.modeId
&& defaultModeId == other.defaultModeId
@@ -342,6 +350,7 @@
overscanTop = other.overscanTop;
overscanRight = other.overscanRight;
overscanBottom = other.overscanBottom;
+ displayCutout = other.displayCutout;
rotation = other.rotation;
modeId = other.modeId;
defaultModeId = other.defaultModeId;
@@ -379,6 +388,7 @@
overscanTop = source.readInt();
overscanRight = source.readInt();
overscanBottom = source.readInt();
+ displayCutout = DisplayCutout.ParcelableWrapper.readCutoutFromParcel(source);
rotation = source.readInt();
modeId = source.readInt();
defaultModeId = source.readInt();
@@ -425,6 +435,7 @@
dest.writeInt(overscanTop);
dest.writeInt(overscanRight);
dest.writeInt(overscanBottom);
+ DisplayCutout.ParcelableWrapper.writeCutoutToParcel(displayCutout, dest, flags);
dest.writeInt(rotation);
dest.writeInt(modeId);
dest.writeInt(defaultModeId);
diff --git a/core/java/android/os/Seccomp.java b/core/java/android/view/IRemoteAnimationFinishedCallback.aidl
similarity index 62%
copy from core/java/android/os/Seccomp.java
copy to core/java/android/view/IRemoteAnimationFinishedCallback.aidl
index f14e93f..ae58b22 100644
--- a/core/java/android/os/Seccomp.java
+++ b/core/java/android/view/IRemoteAnimationFinishedCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -11,14 +11,17 @@
* 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.
+ * limitations under the License
*/
-package android.os;
+package android.view;
/**
- * @hide
+ * Interface to be invoked by the controlling process when a remote animation has finished.
+ *
+ * @see IRemoteAnimationRunner
+ * {@hide}
*/
-public final class Seccomp {
- public static final native void setPolicy();
+interface IRemoteAnimationFinishedCallback {
+ void onAnimationFinished();
}
diff --git a/core/java/android/view/IRemoteAnimationRunner.aidl b/core/java/android/view/IRemoteAnimationRunner.aidl
new file mode 100644
index 0000000..1350ebf
--- /dev/null
+++ b/core/java/android/view/IRemoteAnimationRunner.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.view.RemoteAnimationTarget;
+import android.view.IRemoteAnimationFinishedCallback;
+
+/**
+ * Interface that is used to callback from window manager to the process that runs a remote
+ * animation to start or cancel it.
+ *
+ * {@hide}
+ */
+oneway interface IRemoteAnimationRunner {
+
+ /**
+ * Called when the process needs to start the remote animation.
+ *
+ * @param apps The list of apps to animate.
+ * @param finishedCallback The callback to invoke when the animation is finished.
+ */
+ void onAnimationStart(in RemoteAnimationTarget[] apps,
+ in IRemoteAnimationFinishedCallback finishedCallback);
+
+ /**
+ * Called when the animation was cancelled. From this point on, any updates onto the leashes
+ * won't have any effect anymore.
+ */
+ void onAnimationCancelled();
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 7001067..4adcb8f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -38,6 +38,7 @@
import android.view.IDockedStackListener;
import android.view.IOnKeyguardExitResult;
import android.view.IPinnedStackListener;
+import android.view.RemoteAnimationAdapter;
import android.view.IRotationWatcher;
import android.view.IWallpaperVisibilityListener;
import android.view.IWindowSession;
@@ -124,6 +125,7 @@
void overridePendingAppTransitionMultiThumbFuture(
IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback startedCallback,
boolean scaleUp);
+ void overridePendingAppTransitionRemote(in RemoteAnimationAdapter remoteAnimationAdapter);
void executeAppTransition();
/** Used by system ui to report that recents has shown itself. */
@@ -147,7 +149,7 @@
void exitKeyguardSecurely(IOnKeyguardExitResult callback);
boolean isKeyguardLocked();
boolean isKeyguardSecure();
- void dismissKeyguard(IKeyguardDismissCallback callback);
+ void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message);
// Requires INTERACT_ACROSS_USERS_FULL permission
void setSwitchingUser(boolean switching);
@@ -294,6 +296,11 @@
boolean hasNavigationBar();
/**
+ * Get the position of the nav bar
+ */
+ int getNavBarPosition();
+
+ /**
* Lock the device immediately with the specified options (can be null).
*/
void lockNow(in Bundle options);
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index a2147b7..ed2b8b6 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -804,8 +804,7 @@
public static final int KEYCODE_SYSTEM_NAVIGATION_LEFT = 282;
/** Key code constant: Consumed by the system for navigation right */
public static final int KEYCODE_SYSTEM_NAVIGATION_RIGHT = 283;
- /** Key code constant: Show all apps
- * @hide */
+ /** Key code constant: Show all apps */
public static final int KEYCODE_ALL_APPS = 284;
private static final int LAST_KEYCODE = KEYCODE_ALL_APPS;
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index ab0b3ee..fbba8ab 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -47,6 +47,7 @@
private final int mGravity;
private View mAppName;
private View mHeaderText;
+ private View mSecondaryHeaderText;
private OnClickListener mExpandClickListener;
private HeaderTouchListener mTouchListener = new HeaderTouchListener();
private ImageView mExpandButton;
@@ -58,7 +59,6 @@
private boolean mShowExpandButtonAtEnd;
private boolean mShowWorkBadgeAtEnd;
private Drawable mBackground;
- private int mHeaderBackgroundHeight;
private boolean mEntireHeaderClickable;
private boolean mExpandOnlyOnButton;
private boolean mAcceptAllTouches;
@@ -68,7 +68,7 @@
@Override
public void getOutline(View view, Outline outline) {
if (mBackground != null) {
- outline.setRect(0, 0, getWidth(), mHeaderBackgroundHeight);
+ outline.setRect(0, 0, getWidth(), getHeight());
outline.setAlpha(1f);
}
}
@@ -91,8 +91,6 @@
Resources res = getResources();
mChildMinWidth = res.getDimensionPixelSize(R.dimen.notification_header_shrink_min_width);
mContentEndMargin = res.getDimensionPixelSize(R.dimen.notification_content_margin_end);
- mHeaderBackgroundHeight = res.getDimensionPixelSize(
- R.dimen.notification_header_background_height);
mEntireHeaderClickable = res.getBoolean(R.bool.config_notificationHeaderClickableForExpand);
int[] attrIds = { android.R.attr.gravity };
@@ -106,6 +104,7 @@
super.onFinishInflate();
mAppName = findViewById(com.android.internal.R.id.app_name_text);
mHeaderText = findViewById(com.android.internal.R.id.header_text);
+ mSecondaryHeaderText = findViewById(com.android.internal.R.id.header_text_secondary);
mExpandButton = findViewById(com.android.internal.R.id.expand_button);
mIcon = findViewById(com.android.internal.R.id.icon);
mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
@@ -137,26 +136,33 @@
if (totalWidth > givenWidth) {
int overFlow = totalWidth - givenWidth;
// We are overflowing, lets shrink the app name first
- final int appWidth = mAppName.getMeasuredWidth();
- if (overFlow > 0 && mAppName.getVisibility() != GONE && appWidth > mChildMinWidth) {
- int newSize = appWidth - Math.min(appWidth - mChildMinWidth, overFlow);
- int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
- mAppName.measure(childWidthSpec, wrapContentHeightSpec);
- overFlow -= appWidth - newSize;
- }
- // still overflowing, finaly we shrink the header text
- if (overFlow > 0 && mHeaderText.getVisibility() != GONE) {
- // we're still too big
- final int textWidth = mHeaderText.getMeasuredWidth();
- int newSize = Math.max(0, textWidth - overFlow);
- int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
- mHeaderText.measure(childWidthSpec, wrapContentHeightSpec);
- }
+ overFlow = shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mAppName,
+ mChildMinWidth);
+
+ // still overflowing, we shrink the header text
+ overFlow = shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mHeaderText, 0);
+
+ // still overflowing, finally we shrink the secondary header text
+ shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mSecondaryHeaderText,
+ 0);
}
mTotalWidth = Math.min(totalWidth, givenWidth);
setMeasuredDimension(givenWidth, givenHeight);
}
+ private int shrinkViewForOverflow(int heightSpec, int overFlow, View targetView,
+ int minimumWidth) {
+ final int oldWidth = targetView.getMeasuredWidth();
+ if (overFlow > 0 && targetView.getVisibility() != GONE && oldWidth > minimumWidth) {
+ // we're still too big
+ int newSize = Math.max(minimumWidth, oldWidth - overFlow);
+ int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
+ targetView.measure(childWidthSpec, heightSpec);
+ overFlow -= oldWidth - newSize;
+ }
+ return overFlow;
+ }
+
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left = getPaddingStart();
@@ -228,7 +234,7 @@
@Override
protected void onDraw(Canvas canvas) {
if (mBackground != null) {
- mBackground.setBounds(0, 0, getWidth(), mHeaderBackgroundHeight);
+ mBackground.setBounds(0, 0, getWidth(), getHeight());
mBackground.draw(canvas);
}
}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/core/java/android/view/RemoteAnimationAdapter.aidl
similarity index 76%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to core/java/android/view/RemoteAnimationAdapter.aidl
index d750363..855bc74 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/core/java/android/view/RemoteAnimationAdapter.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -11,10 +11,9 @@
* 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.
+ * limitations under the License
*/
-/** @hide */
-package android.telephony.data;
+package android.view;
-parcelable InterfaceAddress;
+parcelable RemoteAnimationAdapter;
diff --git a/core/java/android/view/RemoteAnimationAdapter.java b/core/java/android/view/RemoteAnimationAdapter.java
new file mode 100644
index 0000000..d597e59
--- /dev/null
+++ b/core/java/android/view/RemoteAnimationAdapter.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view;
+
+import android.app.ActivityOptions;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Object that describes how to run a remote animation.
+ * <p>
+ * A remote animation lets another app control the entire app transition. It does so by
+ * <ul>
+ * <li>using {@link ActivityOptions#makeRemoteAnimation}</li>
+ * <li>using {@link IWindowManager#overridePendingAppTransitionRemote}</li>
+ * </ul>
+ * to register a {@link RemoteAnimationAdapter} that describes how the animation should be run:
+ * Along some meta-data, this object contains a callback that gets invoked from window manager when
+ * the transition is ready to be started.
+ * <p>
+ * Window manager supplies a list of {@link RemoteAnimationTarget}s into the callback. Each target
+ * contains information about the activity that is animating as well as
+ * {@link RemoteAnimationTarget#leash}. The controlling app can modify the leash like any other
+ * {@link SurfaceControl}, including the possibility to synchronize updating the leash's surface
+ * properties with a frame to be drawn using
+ * {@link SurfaceControl.Transaction#deferTransactionUntil}.
+ * <p>
+ * When the animation is done, the controlling app can invoke
+ * {@link IRemoteAnimationFinishedCallback} that gets supplied into
+ * {@link IRemoteAnimationRunner#onStartAnimation}
+ *
+ * @hide
+ */
+public class RemoteAnimationAdapter implements Parcelable {
+
+ private final IRemoteAnimationRunner mRunner;
+ private final long mDuration;
+ private final long mStatusBarTransitionDelay;
+
+ /**
+ * @param runner The interface that gets notified when we actually need to start the animation.
+ * @param duration The duration of the animation.
+ * @param statusBarTransitionDelay The desired delay for all visual animations in the
+ * status bar caused by this app animation in millis.
+ */
+ public RemoteAnimationAdapter(IRemoteAnimationRunner runner, long duration,
+ long statusBarTransitionDelay) {
+ mRunner = runner;
+ mDuration = duration;
+ mStatusBarTransitionDelay = statusBarTransitionDelay;
+ }
+
+ public RemoteAnimationAdapter(Parcel in) {
+ mRunner = IRemoteAnimationRunner.Stub.asInterface(in.readStrongBinder());
+ mDuration = in.readLong();
+ mStatusBarTransitionDelay = in.readLong();
+ }
+
+ public IRemoteAnimationRunner getRunner() {
+ return mRunner;
+ }
+
+ public long getDuration() {
+ return mDuration;
+ }
+
+ public long getStatusBarTransitionDelay() {
+ return mStatusBarTransitionDelay;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStrongInterface(mRunner);
+ dest.writeLong(mDuration);
+ dest.writeLong(mStatusBarTransitionDelay);
+ }
+
+ public static final Creator<RemoteAnimationAdapter> CREATOR
+ = new Creator<RemoteAnimationAdapter>() {
+ public RemoteAnimationAdapter createFromParcel(Parcel in) {
+ return new RemoteAnimationAdapter(in);
+ }
+
+ public RemoteAnimationAdapter[] newArray(int size) {
+ return new RemoteAnimationAdapter[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/core/java/android/view/RemoteAnimationDefinition.aidl
similarity index 76%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to core/java/android/view/RemoteAnimationDefinition.aidl
index d750363..32ecd01 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/core/java/android/view/RemoteAnimationDefinition.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -11,10 +11,9 @@
* 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.
+ * limitations under the License
*/
-/** @hide */
-package android.telephony.data;
+package android.view;
-parcelable InterfaceAddress;
+parcelable RemoteAnimationDefinition;
diff --git a/core/java/android/view/RemoteAnimationDefinition.java b/core/java/android/view/RemoteAnimationDefinition.java
new file mode 100644
index 0000000..381f692
--- /dev/null
+++ b/core/java/android/view/RemoteAnimationDefinition.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+import android.view.WindowManager.TransitionType;
+
+/**
+ * Defines which animation types should be overridden by which remote animation.
+ *
+ * @hide
+ */
+public class RemoteAnimationDefinition implements Parcelable {
+
+ private final SparseArray<RemoteAnimationAdapter> mTransitionAnimationMap;
+
+ public RemoteAnimationDefinition() {
+ mTransitionAnimationMap = new SparseArray<>();
+ }
+
+ /**
+ * Registers a remote animation for a specific transition.
+ *
+ * @param transition The transition type. Must be one of WindowManager.TRANSIT_* values.
+ * @param adapter The adapter that described how to run the remote animation.
+ */
+ public void addRemoteAnimation(@TransitionType int transition, RemoteAnimationAdapter adapter) {
+ mTransitionAnimationMap.put(transition, adapter);
+ }
+
+ /**
+ * Checks whether a remote animation for specific transition is defined.
+ *
+ * @param transition The transition type. Must be one of WindowManager.TRANSIT_* values.
+ * @return Whether this definition has defined a remote animation for the specified transition.
+ */
+ public boolean hasTransition(@TransitionType int transition) {
+ return mTransitionAnimationMap.get(transition) != null;
+ }
+
+ /**
+ * Retrieves the remote animation for a specific transition.
+ *
+ * @param transition The transition type. Must be one of WindowManager.TRANSIT_* values.
+ * @return The remote animation adapter for the specified transition.
+ */
+ public @Nullable RemoteAnimationAdapter getAdapter(@TransitionType int transition) {
+ return mTransitionAnimationMap.get(transition);
+ }
+
+ public RemoteAnimationDefinition(Parcel in) {
+ mTransitionAnimationMap = in.readSparseArray(null /* loader */);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeSparseArray((SparseArray) mTransitionAnimationMap);
+ }
+
+ public static final Creator<RemoteAnimationDefinition> CREATOR =
+ new Creator<RemoteAnimationDefinition>() {
+ public RemoteAnimationDefinition createFromParcel(Parcel in) {
+ return new RemoteAnimationDefinition(in);
+ }
+
+ public RemoteAnimationDefinition[] newArray(int size) {
+ return new RemoteAnimationDefinition[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/core/java/android/view/RemoteAnimationTarget.aidl
similarity index 76%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to core/java/android/view/RemoteAnimationTarget.aidl
index d750363..769bf5e 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/core/java/android/view/RemoteAnimationTarget.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -11,10 +11,9 @@
* 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.
+ * limitations under the License
*/
-/** @hide */
-package android.telephony.data;
+package android.view;
-parcelable InterfaceAddress;
+parcelable RemoteAnimationTarget;
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
new file mode 100644
index 0000000..f39e618
--- /dev/null
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view;
+
+import android.annotation.IntDef;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Describes an activity to be animated as part of a remote animation.
+ *
+ * @hide
+ */
+public class RemoteAnimationTarget implements Parcelable {
+
+ /**
+ * The app is in the set of opening apps of this transition.
+ */
+ public static final int MODE_OPENING = 0;
+
+ /**
+ * The app is in the set of closing apps of this transition.
+ */
+ public static final int MODE_CLOSING = 1;
+
+ @IntDef(prefix = { "MODE_" }, value = {
+ MODE_OPENING,
+ MODE_CLOSING
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Mode {}
+
+ /**
+ * The {@link Mode} to describe whether this app is opening or closing.
+ */
+ public final @Mode int mode;
+
+ /**
+ * The id of the task this app belongs to.
+ */
+ public final int taskId;
+
+ /**
+ * The {@link SurfaceControl} object to actually control the transform of the app.
+ */
+ public final SurfaceControl leash;
+
+ /**
+ * Whether the app is translucent and may reveal apps behind.
+ */
+ public final boolean isTranslucent;
+
+ /**
+ * The clip rect window manager applies when clipping the app's main surface in screen space
+ * coordinates. This is just a hint to the animation runner: If running a clip-rect animation,
+ * anything that extends beyond these bounds will not have any effect. This implies that any
+ * clip-rect animation should likely stop at these bounds.
+ */
+ public final Rect clipRect;
+
+ /**
+ * The index of the element in the tree in prefix order. This should be used for z-layering
+ * to preserve original z-layer order in the hierarchy tree assuming no "boosting" needs to
+ * happen.
+ */
+ public final int prefixOrderIndex;
+
+ /**
+ * The source position of the app, in screen spaces coordinates. If the position of the leash
+ * is modified from the controlling app, any animation transform needs to be offset by this
+ * amount.
+ */
+ public final Point position;
+
+ /**
+ * The bounds of the source container the app lives in, in screen space coordinates. If the crop
+ * of the leash is modified from the controlling app, it needs to take the source container
+ * bounds into account when calculating the crop.
+ */
+ public final Rect sourceContainerBounds;
+
+ public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent,
+ Rect clipRect, int prefixOrderIndex, Point position, Rect sourceContainerBounds) {
+ this.mode = mode;
+ this.taskId = taskId;
+ this.leash = leash;
+ this.isTranslucent = isTranslucent;
+ this.clipRect = new Rect(clipRect);
+ this.prefixOrderIndex = prefixOrderIndex;
+ this.position = new Point(position);
+ this.sourceContainerBounds = new Rect(sourceContainerBounds);
+ }
+
+ public RemoteAnimationTarget(Parcel in) {
+ taskId = in.readInt();
+ mode = in.readInt();
+ leash = in.readParcelable(null);
+ isTranslucent = in.readBoolean();
+ clipRect = in.readParcelable(null);
+ prefixOrderIndex = in.readInt();
+ position = in.readParcelable(null);
+ sourceContainerBounds = in.readParcelable(null);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(taskId);
+ dest.writeInt(mode);
+ dest.writeParcelable(leash, 0 /* flags */);
+ dest.writeBoolean(isTranslucent);
+ dest.writeParcelable(clipRect, 0 /* flags */);
+ dest.writeInt(prefixOrderIndex);
+ dest.writeParcelable(position, 0 /* flags */);
+ dest.writeParcelable(sourceContainerBounds, 0 /* flags */);
+ }
+
+ public static final Creator<RemoteAnimationTarget> CREATOR
+ = new Creator<RemoteAnimationTarget>() {
+ public RemoteAnimationTarget createFromParcel(Parcel in) {
+ return new RemoteAnimationTarget(in);
+ }
+
+ public RemoteAnimationTarget[] newArray(int size) {
+ return new RemoteAnimationTarget[size];
+ }
+ };
+}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 268e460..bd7f8e5 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -16,20 +16,22 @@
package android.view;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
import static android.graphics.Matrix.MSCALE_X;
import static android.graphics.Matrix.MSCALE_Y;
import static android.graphics.Matrix.MSKEW_X;
import static android.graphics.Matrix.MSKEW_Y;
import static android.graphics.Matrix.MTRANS_X;
import static android.graphics.Matrix.MTRANS_Y;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+import static android.view.SurfaceControlProto.HASH_CODE;
+import static android.view.SurfaceControlProto.NAME;
import android.annotation.Size;
import android.graphics.Bitmap;
import android.graphics.GraphicBuffer;
-import android.graphics.PixelFormat;
import android.graphics.Matrix;
+import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
@@ -40,11 +42,13 @@
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.proto.ProtoOutputStream;
import android.view.Surface.OutOfResourcesException;
import com.android.internal.annotations.GuardedBy;
import dalvik.system.CloseGuard;
+
import libcore.util.NativeAllocationRegistry;
import java.io.Closeable;
@@ -628,6 +632,21 @@
nativeWriteToParcel(mNativeObject, dest);
}
+ /**
+ * Write to a protocol buffer output stream. Protocol buffer message definition is at {@link
+ * android.view.SurfaceControlProto}.
+ *
+ * @param proto Stream to write the SurfaceControl object to.
+ * @param fieldId Field Id of the SurfaceControl as defined in the parent message.
+ * @hide
+ */
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(HASH_CODE, System.identityHashCode(this));
+ proto.write(NAME, mName);
+ proto.end(token);
+ }
+
public static final Creator<SurfaceControl> CREATOR
= new Creator<SurfaceControl>() {
public SurfaceControl createFromParcel(Parcel in) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f62189e..f461e45 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3226,6 +3226,11 @@
*/
private static final int PFLAG3_SCREEN_READER_FOCUSABLE = 0x10000000;
+ /**
+ * The last aggregated visibility. Used to detect when it truly changes.
+ */
+ private static final int PFLAG3_AGGREGATED_VISIBLE = 0x20000000;
+
/* End of masks for mPrivateFlags3 */
/**
@@ -3387,6 +3392,18 @@
* decorations when they are shown. You can perform layout of your inner
* UI elements to account for non-fullscreen system UI through the
* {@link #fitSystemWindows(Rect)} method.
+ *
+ * <p>Note: on displays that have a {@link DisplayCutout}, the window may still be placed
+ * differently than if {@link #SYSTEM_UI_FLAG_FULLSCREEN} was set, if the
+ * window's {@link WindowManager.LayoutParams#layoutInDisplayCutoutMode
+ * layoutInDisplayCutoutMode} is
+ * {@link WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
+ * LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT}. To avoid this, use either of the other modes.
+ *
+ * @see WindowManager.LayoutParams#layoutInDisplayCutoutMode
+ * @see WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
+ * @see WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ * @see WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
*/
public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400;
@@ -4045,6 +4062,12 @@
private CharSequence mContentDescription;
/**
+ * If this view represents a distinct part of the window, it can have a title that labels the
+ * area.
+ */
+ private CharSequence mAccessibilityPaneTitle;
+
+ /**
* Specifies the id of a view for which this view serves as a label for
* accessibility purposes.
*/
@@ -4187,6 +4210,11 @@
*/
private static boolean sCanFocusZeroSized;
+ /**
+ * Always assign focus if a focusable View is available.
+ */
+ private static boolean sAlwaysAssignFocus;
+
private String mTransitionName;
static class TintInfo {
@@ -4412,7 +4440,6 @@
private CheckForLongPress mPendingCheckForLongPress;
private CheckForTap mPendingCheckForTap = null;
private PerformClick mPerformClick;
- private SendViewScrolledAccessibilityEvent mSendViewScrolledAccessibilityEvent;
private UnsetPressedState mUnsetPressedState;
@@ -4805,6 +4832,8 @@
sCanFocusZeroSized = targetSdkVersion < Build.VERSION_CODES.P;
+ sAlwaysAssignFocus = targetSdkVersion < Build.VERSION_CODES.P;
+
sCompatibilityDone = true;
}
}
@@ -5409,6 +5438,11 @@
setScreenReaderFocusable(a.getBoolean(attr, false));
}
break;
+ case R.styleable.View_accessibilityPaneTitle:
+ if (a.peekValue(attr) != null) {
+ setAccessibilityPaneTitle(a.getString(attr));
+ }
+ break;
}
}
@@ -6990,8 +7024,8 @@
* Called when this view wants to give up focus. If focus is cleared
* {@link #onFocusChanged(boolean, int, android.graphics.Rect)} is called.
* <p>
- * <strong>Note:</strong> When a View clears focus the framework is trying
- * to give focus to the first focusable View from the top. Hence, if this
+ * <strong>Note:</strong> When not in touch-mode, the framework will try to give focus
+ * to the first focusable View from the top after focus is cleared. Hence, if this
* View is the first from the top that can take focus, then all callbacks
* related to clearing focus will be invoked after which the framework will
* give focus to this view.
@@ -7002,7 +7036,8 @@
System.out.println(this + " clearFocus()");
}
- clearFocusInternal(null, true, true);
+ final boolean refocus = sAlwaysAssignFocus || !isInTouchMode();
+ clearFocusInternal(null, true, refocus);
}
/**
@@ -7164,7 +7199,7 @@
if (gainFocus) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
} else {
- notifyViewAccessibilityStateChangedIfNeeded(
+ notifyAccessibilityStateChanged(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
@@ -7197,20 +7232,24 @@
notifyEnterOrExitForAutoFillIfNeeded(gainFocus);
}
- private void notifyEnterOrExitForAutoFillIfNeeded(boolean enter) {
- if (isAutofillable() && isAttachedToWindow()) {
+ /** @hide */
+ public void notifyEnterOrExitForAutoFillIfNeeded(boolean enter) {
+ if (canNotifyAutofillEnterExitEvent()) {
AutofillManager afm = getAutofillManager();
if (afm != null) {
- if (enter && hasWindowFocus() && isFocused()) {
+ if (enter && isFocused()) {
// We have not been laid out yet, hence cannot evaluate
// whether this view is visible to the user, we will do
// the evaluation once layout is complete.
if (!isLaidOut()) {
mPrivateFlags3 |= PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
} else if (isVisibleToUser()) {
+ // TODO This is a potential problem that View gets focus before it's visible
+ // to User. Ideally View should handle the event when isVisibleToUser()
+ // becomes true where it should issue notifyViewEntered().
afm.notifyViewEntered(this);
}
- } else if (!hasWindowFocus() || !isFocused()) {
+ } else if (!isFocused()) {
afm.notifyViewExited(this);
}
}
@@ -7218,6 +7257,34 @@
}
/**
+ * If this view is a visually distinct portion of a window, for example the content view of
+ * a fragment that is replaced, it is considered a pane for accessibility purposes. In order
+ * for accessibility services to understand the views role, and to announce its title as
+ * appropriate, such views should have pane titles.
+ *
+ * @param accessibilityPaneTitle The pane's title.
+ *
+ * {@see AccessibilityNodeInfo#setPaneTitle(CharSequence)}
+ */
+ public void setAccessibilityPaneTitle(CharSequence accessibilityPaneTitle) {
+ if (!TextUtils.equals(accessibilityPaneTitle, mAccessibilityPaneTitle)) {
+ mAccessibilityPaneTitle = accessibilityPaneTitle;
+ notifyAccessibilityStateChanged(AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_TITLE);
+ }
+ }
+
+ /**
+ * Get the title of the pane for purposes of accessibility.
+ *
+ * @return The current pane title.
+ *
+ * {@see #setAccessibilityPaneTitle}.
+ */
+ public CharSequence getAccessibilityPaneTitle() {
+ return mAccessibilityPaneTitle;
+ }
+
+ /**
* Sends an accessibility event of the given type. If accessibility is
* not enabled this method has no effect. The default implementation calls
* {@link #onInitializeAccessibilityEvent(AccessibilityEvent)} first
@@ -7316,7 +7383,12 @@
* @hide
*/
public void sendAccessibilityEventUncheckedInternal(AccessibilityEvent event) {
- if (!isShown()) {
+ // Panes disappearing are relevant even if though the view is no longer visible.
+ boolean isWindowStateChanged =
+ (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ boolean isWindowDisappearedEvent = isWindowStateChanged && ((event.getContentChangeTypes()
+ & AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED) != 0);
+ if (!isShown() && !isWindowDisappearedEvent) {
return;
}
onInitializeAccessibilityEvent(event);
@@ -7424,6 +7496,10 @@
* @hide
*/
public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) {
+ if ((event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)
+ && !TextUtils.isEmpty(getAccessibilityPaneTitle())) {
+ event.getText().add(getAccessibilityPaneTitle());
+ }
}
/**
@@ -8245,6 +8321,11 @@
&& getAutofillViewId() > LAST_APP_AUTOFILL_ID;
}
+ /** @hide */
+ public boolean canNotifyAutofillEnterExitEvent() {
+ return isAutofillable() && isAttachedToWindow();
+ }
+
private void populateVirtualStructure(ViewStructure structure,
AccessibilityNodeProvider provider, AccessibilityNodeInfo info) {
structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()),
@@ -8514,6 +8595,7 @@
info.addAction(AccessibilityAction.ACTION_SHOW_ON_SCREEN);
populateAccessibilityNodeInfoDrawingOrderInParent(info);
+ info.setPaneTitle(mAccessibilityPaneTitle);
}
/**
@@ -8834,9 +8916,9 @@
final boolean nonEmptyDesc = contentDescription != null && contentDescription.length() > 0;
if (nonEmptyDesc && getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ notifyAccessibilitySubtreeChanged();
} else {
- notifyViewAccessibilityStateChangedIfNeeded(
+ notifyAccessibilityStateChanged(
AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION);
}
}
@@ -8869,7 +8951,7 @@
return;
}
mAccessibilityTraversalBeforeId = beforeId;
- notifyViewAccessibilityStateChangedIfNeeded(
+ notifyAccessibilityStateChanged(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
@@ -8913,7 +8995,7 @@
return;
}
mAccessibilityTraversalAfterId = afterId;
- notifyViewAccessibilityStateChangedIfNeeded(
+ notifyAccessibilityStateChanged(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
@@ -8956,7 +9038,7 @@
&& mID == View.NO_ID) {
mID = generateViewId();
}
- notifyViewAccessibilityStateChangedIfNeeded(
+ notifyAccessibilityStateChanged(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
@@ -10457,8 +10539,7 @@
if (pflags3 != mPrivateFlags3) {
mPrivateFlags3 = pflags3;
- notifyViewAccessibilityStateChangedIfNeeded(
- AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+ notifyAccessibilityStateChanged(AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
@@ -11286,7 +11367,7 @@
mPrivateFlags2 &= ~PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK;
mPrivateFlags2 |= (mode << PFLAG2_ACCESSIBILITY_LIVE_REGION_SHIFT)
& PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK;
- notifyViewAccessibilityStateChangedIfNeeded(
+ notifyAccessibilityStateChanged(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
@@ -11344,9 +11425,9 @@
mPrivateFlags2 |= (mode << PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT)
& PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK;
if (!maySkipNotify || oldIncludeForAccessibility != includeForAccessibility()) {
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ notifyAccessibilitySubtreeChanged();
} else {
- notifyViewAccessibilityStateChangedIfNeeded(
+ notifyAccessibilityStateChanged(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
@@ -11405,6 +11486,7 @@
* {@link #getAccessibilityLiveRegion()} is not
* {@link #ACCESSIBILITY_LIVE_REGION_NONE}.
* </ul>
+ * <li>Has an accessibility pane title, see {@link #setAccessibilityPaneTitle}</li>
* </ol>
*
* @return Whether the view is exposed for accessibility.
@@ -11431,7 +11513,8 @@
return mode == IMPORTANT_FOR_ACCESSIBILITY_YES || isActionableForAccessibility()
|| hasListenersForAccessibility() || getAccessibilityNodeProvider() != null
- || getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE;
+ || getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE
+ || (mAccessibilityPaneTitle != null);
}
/**
@@ -11521,25 +11604,8 @@
*
* @hide
*/
- public void notifyViewAccessibilityStateChangedIfNeeded(int changeType) {
- if (!AccessibilityManager.getInstance(mContext).isEnabled() || mAttachInfo == null) {
- return;
- }
- // If this is a live region, we should send a subtree change event
- // from this view immediately. Otherwise, we can let it propagate up.
- if (getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE) {
- final AccessibilityEvent event = AccessibilityEvent.obtain();
- event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
- event.setContentChangeTypes(changeType);
- sendAccessibilityEventUnchecked(event);
- } else if (mParent != null) {
- try {
- mParent.notifySubtreeAccessibilityStateChanged(this, this, changeType);
- } catch (AbstractMethodError e) {
- Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
- " does not fully implement ViewParent", e);
- }
- }
+ public void notifyAccessibilityStateChanged(int changeType) {
+ notifyAccessibilityStateChanged(this, changeType);
}
/**
@@ -11553,22 +11619,42 @@
*
* @hide
*/
- public void notifySubtreeAccessibilityStateChangedIfNeeded() {
+ public void notifyAccessibilitySubtreeChanged() {
+ if ((mPrivateFlags2 & PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED) == 0) {
+ mPrivateFlags2 |= PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED;
+ notifyAccessibilityStateChanged(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
+ }
+ }
+
+ void notifyAccessibilityStateChanged(View source, int changeType) {
if (!AccessibilityManager.getInstance(mContext).isEnabled() || mAttachInfo == null) {
return;
}
- if ((mPrivateFlags2 & PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED) == 0) {
- mPrivateFlags2 |= PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED;
+ // Changes to views with a pane title count as window state changes, as the pane title
+ // marks them as significant parts of the UI.
+ if (!TextUtils.isEmpty(getAccessibilityPaneTitle())) {
+ final AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setEventType(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ event.setContentChangeTypes(changeType);
+ onPopulateAccessibilityEvent(event);
if (mParent != null) {
try {
- mParent.notifySubtreeAccessibilityStateChanged(
- this, this, AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
+ mParent.requestSendAccessibilityEvent(this, event);
} catch (AbstractMethodError e) {
- Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
- " does not fully implement ViewParent", e);
+ Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName()
+ + " does not fully implement ViewParent", e);
}
}
}
+
+ if (mParent != null) {
+ try {
+ mParent.notifySubtreeAccessibilityStateChanged(this, source, changeType);
+ } catch (AbstractMethodError e) {
+ Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName()
+ + " does not fully implement ViewParent", e);
+ }
+ }
}
/**
@@ -11588,8 +11674,10 @@
/**
* Reset the flag indicating the accessibility state of the subtree rooted
* at this view changed.
+ *
+ * @hide
*/
- void resetSubtreeAccessibilityStateChanged() {
+ public void resetSubtreeAccessibilityStateChanged() {
mPrivateFlags2 &= ~PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED;
}
@@ -11750,7 +11838,7 @@
|| getAccessibilitySelectionEnd() != end)
&& (start == end)) {
setAccessibilitySelection(start, end);
- notifyViewAccessibilityStateChangedIfNeeded(
+ notifyAccessibilityStateChanged(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
return true;
}
@@ -12372,8 +12460,6 @@
imm.focusIn(this);
}
- notifyEnterOrExitForAutoFillIfNeeded(hasWindowFocus);
-
refreshDrawableState();
}
@@ -12492,6 +12578,10 @@
*/
@CallSuper
public void onVisibilityAggregated(boolean isVisible) {
+ // Update our internal visibility tracking so we can detect changes
+ boolean oldVisible = (mPrivateFlags3 & PFLAG3_AGGREGATED_VISIBLE) != 0;
+ mPrivateFlags3 = isVisible ? (mPrivateFlags3 | PFLAG3_AGGREGATED_VISIBLE)
+ : (mPrivateFlags3 & ~PFLAG3_AGGREGATED_VISIBLE);
if (isVisible && mAttachInfo != null) {
initialAwakenScrollBars();
}
@@ -12532,6 +12622,13 @@
}
}
}
+ if (!TextUtils.isEmpty(getAccessibilityPaneTitle())) {
+ if (isVisible != oldVisible) {
+ notifyAccessibilityStateChanged(isVisible
+ ? AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_APPEARED
+ : AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED);
+ }
+ }
}
/**
@@ -13744,7 +13841,7 @@
((!(mParent instanceof ViewGroup)) || ((ViewGroup) mParent).isShown())) {
dispatchVisibilityAggregated(newVisibility == VISIBLE);
}
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ notifyAccessibilitySubtreeChanged();
}
}
@@ -13790,13 +13887,13 @@
|| (changed & CLICKABLE) != 0 || (changed & LONG_CLICKABLE) != 0
|| (changed & CONTEXT_CLICKABLE) != 0) {
if (oldIncludeForAccessibility != includeForAccessibility()) {
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ notifyAccessibilitySubtreeChanged();
} else {
- notifyViewAccessibilityStateChangedIfNeeded(
+ notifyAccessibilityStateChanged(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
} else if ((changed & ENABLED_MASK) != 0) {
- notifyViewAccessibilityStateChangedIfNeeded(
+ notifyAccessibilityStateChanged(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
@@ -13831,10 +13928,13 @@
* @param oldt Previous vertical scroll origin.
*/
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ notifyAccessibilitySubtreeChanged();
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- postSendViewScrolledAccessibilityEventCallback(l - oldl, t - oldt);
+ ViewRootImpl root = getViewRootImpl();
+ if (root != null) {
+ root.getAccessibilityState()
+ .getSendViewScrolledAccessibilityEvent()
+ .post(this, /* dx */ l - oldl, /* dy */ t - oldt);
}
mBackgroundSizeChanged = true;
@@ -14230,7 +14330,7 @@
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ notifyAccessibilitySubtreeChanged();
}
}
@@ -14274,7 +14374,7 @@
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ notifyAccessibilitySubtreeChanged();
}
}
@@ -14318,7 +14418,7 @@
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ notifyAccessibilitySubtreeChanged();
}
}
@@ -14355,7 +14455,7 @@
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ notifyAccessibilitySubtreeChanged();
}
}
@@ -14392,7 +14492,7 @@
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ notifyAccessibilitySubtreeChanged();
}
}
@@ -14595,7 +14695,7 @@
if (mTransformationInfo.mAlpha != alpha) {
// Report visibility changes, which can affect children, to accessibility
if ((alpha == 0) ^ (mTransformationInfo.mAlpha == 0)) {
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ notifyAccessibilitySubtreeChanged();
}
mTransformationInfo.mAlpha = alpha;
if (onSetAlpha((int) (alpha * 255))) {
@@ -15097,7 +15197,7 @@
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ notifyAccessibilitySubtreeChanged();
}
}
@@ -15131,7 +15231,7 @@
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ notifyAccessibilitySubtreeChanged();
}
}
@@ -15301,7 +15401,7 @@
public void invalidateOutline() {
rebuildOutline();
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ notifyAccessibilitySubtreeChanged();
invalidateViewProperty(false, false);
}
@@ -15496,7 +15596,7 @@
}
invalidateParentIfNeeded();
}
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ notifyAccessibilitySubtreeChanged();
}
}
@@ -15544,7 +15644,7 @@
}
invalidateParentIfNeeded();
}
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ notifyAccessibilitySubtreeChanged();
}
}
@@ -16422,18 +16522,6 @@
}
/**
- * Post a callback to send a {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
- * This event is sent at most once every
- * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}.
- */
- private void postSendViewScrolledAccessibilityEventCallback(int dx, int dy) {
- if (mSendViewScrolledAccessibilityEvent == null) {
- mSendViewScrolledAccessibilityEvent = new SendViewScrolledAccessibilityEvent();
- }
- mSendViewScrolledAccessibilityEvent.post(dx, dy);
- }
-
- /**
* Called by a parent to request that a child update its values for mScrollX
* and mScrollY if necessary. This will typically be done if the child is
* animating a scroll using a {@link android.widget.Scroller Scroller}
@@ -17688,7 +17776,13 @@
removeUnsetPressCallback();
removeLongPressCallback();
removePerformClickCallback();
- cancel(mSendViewScrolledAccessibilityEvent);
+ if (mAttachInfo != null
+ && mAttachInfo.mViewRootImpl.mAccessibilityState != null
+ && mAttachInfo.mViewRootImpl.mAccessibilityState.isScrollEventSenderInitialized()) {
+ mAttachInfo.mViewRootImpl.mAccessibilityState
+ .getSendViewScrolledAccessibilityEvent()
+ .cancelIfPendingFor(this);
+ }
stopNestedScroll();
// Anything that started animating right before detach should already
@@ -20307,7 +20401,7 @@
mForegroundInfo.mBoundsChanged = true;
}
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ notifyAccessibilitySubtreeChanged();
}
return changed;
}
@@ -21751,7 +21845,7 @@
if (selected) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
} else {
- notifyViewAccessibilityStateChangedIfNeeded(
+ notifyAccessibilityStateChanged(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
@@ -26308,53 +26402,6 @@
}
/**
- * Resuable callback for sending
- * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} accessibility event.
- */
- private class SendViewScrolledAccessibilityEvent implements Runnable {
- public volatile boolean mIsPending;
- public int mDeltaX;
- public int mDeltaY;
-
- public void post(int dx, int dy) {
- mDeltaX += dx;
- mDeltaY += dy;
- if (!mIsPending) {
- mIsPending = true;
- postDelayed(this, ViewConfiguration.getSendRecurringAccessibilityEventsInterval());
- }
- }
-
- @Override
- public void run() {
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- AccessibilityEvent event = AccessibilityEvent.obtain(
- AccessibilityEvent.TYPE_VIEW_SCROLLED);
- event.setScrollDeltaX(mDeltaX);
- event.setScrollDeltaY(mDeltaY);
- sendAccessibilityEventUnchecked(event);
- }
- reset();
- }
-
- private void reset() {
- mIsPending = false;
- mDeltaX = 0;
- mDeltaY = 0;
- }
- }
-
- /**
- * Remove the pending callback for sending a
- * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} accessibility event.
- */
- private void cancel(@Nullable SendViewScrolledAccessibilityEvent callback) {
- if (callback == null || !callback.mIsPending) return;
- removeCallbacks(callback);
- callback.reset();
- }
-
- /**
* <p>
* This class represents a delegate that can be registered in a {@link View}
* to enhance accessibility support via composition rather via inheritance.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 703364f..e0864bd 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3646,44 +3646,34 @@
return ViewGroup.class.getName();
}
- @Override
- public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) {
- // If this is a live region, we should send a subtree change event
- // from this view. Otherwise, we can let it propagate up.
- if (getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE) {
- notifyViewAccessibilityStateChangedIfNeeded(
- AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
- } else if (mParent != null) {
- try {
- mParent.notifySubtreeAccessibilityStateChanged(this, source, changeType);
- } catch (AbstractMethodError e) {
- Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
- " does not fully implement ViewParent", e);
- }
- }
- }
-
/** @hide */
@Override
- public void notifySubtreeAccessibilityStateChangedIfNeeded() {
+ public void notifyAccessibilitySubtreeChanged() {
if (!AccessibilityManager.getInstance(mContext).isEnabled() || mAttachInfo == null) {
return;
}
// If something important for a11y is happening in this subtree, make sure it's dispatched
// from a view that is important for a11y so it doesn't get lost.
- if ((getImportantForAccessibility() != IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS)
- && !isImportantForAccessibility() && (getChildCount() > 0)) {
+ if (getImportantForAccessibility() != IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ && !isImportantForAccessibility()
+ && getChildCount() > 0) {
ViewParent a11yParent = getParentForAccessibility();
if (a11yParent instanceof View) {
- ((View) a11yParent).notifySubtreeAccessibilityStateChangedIfNeeded();
+ ((View) a11yParent).notifyAccessibilitySubtreeChanged();
return;
}
}
- super.notifySubtreeAccessibilityStateChangedIfNeeded();
+ super.notifyAccessibilitySubtreeChanged();
}
@Override
- void resetSubtreeAccessibilityStateChanged() {
+ public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) {
+ notifyAccessibilityStateChanged(source, changeType);
+ }
+
+ /** @hide */
+ @Override
+ public void resetSubtreeAccessibilityStateChanged() {
super.resetSubtreeAccessibilityStateChanged();
View[] children = mChildren;
final int childCount = mChildrenCount;
@@ -5095,7 +5085,7 @@
}
if (child.getVisibility() != View.GONE) {
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ notifyAccessibilitySubtreeChanged();
}
if (mTransientIndices != null) {
@@ -5365,7 +5355,7 @@
dispatchViewRemoved(view);
if (view.getVisibility() != View.GONE) {
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ notifyAccessibilitySubtreeChanged();
}
int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
@@ -6084,7 +6074,7 @@
if (invalidate) {
invalidateViewProperty(false, false);
}
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ notifyAccessibilitySubtreeChanged();
}
@Override
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 6c5091c..30f584c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -20,7 +20,7 @@
import static android.view.View.PFLAG_DRAW_ANIMATION;
import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER;
import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM;
-import static android.view.WindowManager.LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
@@ -89,9 +89,11 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.accessibility.AccessibilityViewHierarchyState;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
+import android.view.accessibility.ThrottlingAccessibilityEventSender;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.inputmethod.InputMethodManager;
@@ -113,7 +115,6 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.concurrent.CountDownLatch;
/**
@@ -460,10 +461,6 @@
new AccessibilityInteractionConnectionManager();
final HighContrastTextManager mHighContrastTextManager;
- SendWindowContentChangedAccessibilityEvent mSendWindowContentChangedAccessibilityEvent;
-
- HashSet<View> mTempHashSet;
-
private final int mDensity;
private final int mNoncompatDensity;
@@ -478,6 +475,8 @@
private boolean mNeedsRendererSetup;
+ protected AccessibilityViewHierarchyState mAccessibilityState;
+
/**
* Consistency verifier for debugging purposes.
*/
@@ -531,7 +530,7 @@
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
if (!sCompatibilityDone) {
- sAlwaysAssignFocus = true;
+ sAlwaysAssignFocus = mTargetSdkVersion < Build.VERSION_CODES.P;
sCompatibilityDone = true;
}
@@ -1597,9 +1596,9 @@
void dispatchApplyInsets(View host) {
WindowInsets insets = getWindowInsets(true /* forceConstruct */);
- final boolean layoutInCutout =
- (mWindowAttributes.flags2 & FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA) != 0;
- if (!layoutInCutout) {
+ final boolean dispatchCutout = (mWindowAttributes.layoutInDisplayCutoutMode
+ == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS);
+ if (!dispatchCutout) {
// Window is either not laid out in cutout or the status bar inset takes care of
// clearing the cutout, so we don't need to dispatch the cutout to the hierarchy.
insets = insets.consumeDisplayCutout();
@@ -2338,7 +2337,7 @@
}
if (mFirst) {
- if (sAlwaysAssignFocus) {
+ if (sAlwaysAssignFocus || !isInTouchMode()) {
// handle first focus request
if (DEBUG_INPUT_RESIZE) {
Log.v(mTag, "First: mView.hasFocus()=" + mView.hasFocus());
@@ -3609,7 +3608,7 @@
checkThread();
if (mView != null) {
if (!mView.hasFocus()) {
- if (sAlwaysAssignFocus) {
+ if (sAlwaysAssignFocus || !isInTouchMode()) {
v.requestFocus();
}
} else {
@@ -4212,10 +4211,7 @@
// find the best view to give focus to in this brave new non-touch-mode
// world
- final View focused = focusSearch(null, View.FOCUS_DOWN);
- if (focused != null) {
- return focused.requestFocus(View.FOCUS_DOWN);
- }
+ return mView.restoreDefaultFocus();
}
return false;
}
@@ -7262,11 +7258,9 @@
* {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}.
*/
private void postSendWindowContentChangedCallback(View source, int changeType) {
- if (mSendWindowContentChangedAccessibilityEvent == null) {
- mSendWindowContentChangedAccessibilityEvent =
- new SendWindowContentChangedAccessibilityEvent();
- }
- mSendWindowContentChangedAccessibilityEvent.runOrPost(source, changeType);
+ getAccessibilityState()
+ .getSendWindowContentChangedAccessibilityEvent()
+ .runOrPost(source, changeType);
}
/**
@@ -7274,11 +7268,20 @@
* {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event.
*/
private void removeSendWindowContentChangedCallback() {
- if (mSendWindowContentChangedAccessibilityEvent != null) {
- mHandler.removeCallbacks(mSendWindowContentChangedAccessibilityEvent);
+ if (mAccessibilityState != null
+ && mAccessibilityState.isWindowContentChangedEventSenderInitialized()) {
+ ThrottlingAccessibilityEventSender.cancelIfPending(
+ mAccessibilityState.getSendWindowContentChangedAccessibilityEvent());
}
}
+ AccessibilityViewHierarchyState getAccessibilityState() {
+ if (mAccessibilityState == null) {
+ mAccessibilityState = new AccessibilityViewHierarchyState();
+ }
+ return mAccessibilityState;
+ }
+
@Override
public boolean showContextMenuForChild(View originalView) {
return false;
@@ -7314,12 +7317,8 @@
return false;
}
- // Immediately flush pending content changed event (if any) to preserve event order
- if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
- && mSendWindowContentChangedAccessibilityEvent != null
- && mSendWindowContentChangedAccessibilityEvent.mSource != null) {
- mSendWindowContentChangedAccessibilityEvent.removeCallbacksAndRun();
- }
+ // Send any pending event to prevent reordering
+ flushPendingAccessibilityEvents();
// Intercept accessibility focus events fired by virtual nodes to keep
// track of accessibility focus position in such nodes.
@@ -7363,6 +7362,19 @@
return true;
}
+ /** @hide */
+ public void flushPendingAccessibilityEvents() {
+ if (mAccessibilityState != null) {
+ if (mAccessibilityState.isScrollEventSenderInitialized()) {
+ mAccessibilityState.getSendViewScrolledAccessibilityEvent().sendNowIfPending();
+ }
+ if (mAccessibilityState.isWindowContentChangedEventSenderInitialized()) {
+ mAccessibilityState.getSendWindowContentChangedAccessibilityEvent()
+ .sendNowIfPending();
+ }
+ }
+ }
+
/**
* Updates the focused virtual view, when necessary, in response to a
* content changed event.
@@ -7497,39 +7509,6 @@
return View.TEXT_ALIGNMENT_RESOLVED_DEFAULT;
}
- private View getCommonPredecessor(View first, View second) {
- if (mTempHashSet == null) {
- mTempHashSet = new HashSet<View>();
- }
- HashSet<View> seen = mTempHashSet;
- seen.clear();
- View firstCurrent = first;
- while (firstCurrent != null) {
- seen.add(firstCurrent);
- ViewParent firstCurrentParent = firstCurrent.mParent;
- if (firstCurrentParent instanceof View) {
- firstCurrent = (View) firstCurrentParent;
- } else {
- firstCurrent = null;
- }
- }
- View secondCurrent = second;
- while (secondCurrent != null) {
- if (seen.contains(secondCurrent)) {
- seen.clear();
- return secondCurrent;
- }
- ViewParent secondCurrentParent = secondCurrent.mParent;
- if (secondCurrentParent instanceof View) {
- secondCurrent = (View) secondCurrentParent;
- } else {
- secondCurrent = null;
- }
- }
- seen.clear();
- return null;
- }
-
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
@@ -8140,80 +8119,6 @@
}
}
- private class SendWindowContentChangedAccessibilityEvent implements Runnable {
- private int mChangeTypes = 0;
-
- public View mSource;
- public long mLastEventTimeMillis;
-
- @Override
- public void run() {
- // Protect against re-entrant code and attempt to do the right thing in the case that
- // we're multithreaded.
- View source = mSource;
- mSource = null;
- if (source == null) {
- Log.e(TAG, "Accessibility content change has no source");
- return;
- }
- // The accessibility may be turned off while we were waiting so check again.
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- mLastEventTimeMillis = SystemClock.uptimeMillis();
- AccessibilityEvent event = AccessibilityEvent.obtain();
- event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
- event.setContentChangeTypes(mChangeTypes);
- source.sendAccessibilityEventUnchecked(event);
- } else {
- mLastEventTimeMillis = 0;
- }
- // In any case reset to initial state.
- source.resetSubtreeAccessibilityStateChanged();
- mChangeTypes = 0;
- }
-
- public void runOrPost(View source, int changeType) {
- if (mHandler.getLooper() != Looper.myLooper()) {
- CalledFromWrongThreadException e = new CalledFromWrongThreadException("Only the "
- + "original thread that created a view hierarchy can touch its views.");
- // TODO: Throw the exception
- Log.e(TAG, "Accessibility content change on non-UI thread. Future Android "
- + "versions will throw an exception.", e);
- // Attempt to recover. This code does not eliminate the thread safety issue, but
- // it should force any issues to happen near the above log.
- mHandler.removeCallbacks(this);
- if (mSource != null) {
- // Dispatch whatever was pending. It's still possible that the runnable started
- // just before we removed the callbacks, and bad things will happen, but at
- // least they should happen very close to the logged error.
- run();
- }
- }
- if (mSource != null) {
- // If there is no common predecessor, then mSource points to
- // a removed view, hence in this case always prefer the source.
- View predecessor = getCommonPredecessor(mSource, source);
- mSource = (predecessor != null) ? predecessor : source;
- mChangeTypes |= changeType;
- return;
- }
- mSource = source;
- mChangeTypes = changeType;
- final long timeSinceLastMillis = SystemClock.uptimeMillis() - mLastEventTimeMillis;
- final long minEventIntevalMillis =
- ViewConfiguration.getSendRecurringAccessibilityEventsInterval();
- if (timeSinceLastMillis >= minEventIntevalMillis) {
- removeCallbacksAndRun();
- } else {
- mHandler.postDelayed(this, minEventIntevalMillis - timeSinceLastMillis);
- }
- }
-
- public void removeCallbacksAndRun() {
- mHandler.removeCallbacks(this);
- run();
- }
- }
-
private static class KeyFallbackManager {
// This is used to ensure that key-fallback events are only dispatched once. We attempt
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index a65aba1..3bb3a4c 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -21,7 +21,6 @@
import static android.view.WindowLayoutParamsProto.BUTTON_BRIGHTNESS;
import static android.view.WindowLayoutParamsProto.COLOR_MODE;
import static android.view.WindowLayoutParamsProto.FLAGS;
-import static android.view.WindowLayoutParamsProto.FLAGS_EXTRA;
import static android.view.WindowLayoutParamsProto.FORMAT;
import static android.view.WindowLayoutParamsProto.GRAVITY;
import static android.view.WindowLayoutParamsProto.HAS_SYSTEM_UI_LISTENERS;
@@ -46,7 +45,6 @@
import android.Manifest.permission;
import android.annotation.IntDef;
-import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -107,6 +105,191 @@
final static String INPUT_CONSUMER_WALLPAPER = "wallpaper_input_consumer";
/**
+ * Not set up for a transition.
+ * @hide
+ */
+ int TRANSIT_UNSET = -1;
+
+ /**
+ * No animation for transition.
+ * @hide
+ */
+ int TRANSIT_NONE = 0;
+
+ /**
+ * A window in a new activity is being opened on top of an existing one in the same task.
+ * @hide
+ */
+ int TRANSIT_ACTIVITY_OPEN = 6;
+
+ /**
+ * The window in the top-most activity is being closed to reveal the previous activity in the
+ * same task.
+ * @hide
+ */
+ int TRANSIT_ACTIVITY_CLOSE = 7;
+
+ /**
+ * A window in a new task is being opened on top of an existing one in another activity's task.
+ * @hide
+ */
+ int TRANSIT_TASK_OPEN = 8;
+
+ /**
+ * A window in the top-most activity is being closed to reveal the previous activity in a
+ * different task.
+ * @hide
+ */
+ int TRANSIT_TASK_CLOSE = 9;
+
+ /**
+ * A window in an existing task is being displayed on top of an existing one in another
+ * activity's task.
+ * @hide
+ */
+ int TRANSIT_TASK_TO_FRONT = 10;
+
+ /**
+ * A window in an existing task is being put below all other tasks.
+ * @hide
+ */
+ int TRANSIT_TASK_TO_BACK = 11;
+
+ /**
+ * A window in a new activity that doesn't have a wallpaper is being opened on top of one that
+ * does, effectively closing the wallpaper.
+ * @hide
+ */
+ int TRANSIT_WALLPAPER_CLOSE = 12;
+
+ /**
+ * A window in a new activity that does have a wallpaper is being opened on one that didn't,
+ * effectively opening the wallpaper.
+ * @hide
+ */
+ int TRANSIT_WALLPAPER_OPEN = 13;
+
+ /**
+ * A window in a new activity is being opened on top of an existing one, and both are on top
+ * of the wallpaper.
+ * @hide
+ */
+ int TRANSIT_WALLPAPER_INTRA_OPEN = 14;
+
+ /**
+ * The window in the top-most activity is being closed to reveal the previous activity, and
+ * both are on top of the wallpaper.
+ * @hide
+ */
+ int TRANSIT_WALLPAPER_INTRA_CLOSE = 15;
+
+ /**
+ * A window in a new task is being opened behind an existing one in another activity's task.
+ * The new window will show briefly and then be gone.
+ * @hide
+ */
+ int TRANSIT_TASK_OPEN_BEHIND = 16;
+
+ /**
+ * A window in a task is being animated in-place.
+ * @hide
+ */
+ int TRANSIT_TASK_IN_PLACE = 17;
+
+ /**
+ * An activity is being relaunched (e.g. due to configuration change).
+ * @hide
+ */
+ int TRANSIT_ACTIVITY_RELAUNCH = 18;
+
+ /**
+ * A task is being docked from recents.
+ * @hide
+ */
+ int TRANSIT_DOCK_TASK_FROM_RECENTS = 19;
+
+ /**
+ * Keyguard is going away.
+ * @hide
+ */
+ int TRANSIT_KEYGUARD_GOING_AWAY = 20;
+
+ /**
+ * Keyguard is going away with showing an activity behind that requests wallpaper.
+ * @hide
+ */
+ int TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER = 21;
+
+ /**
+ * Keyguard is being occluded.
+ * @hide
+ */
+ int TRANSIT_KEYGUARD_OCCLUDE = 22;
+
+ /**
+ * Keyguard is being unoccluded.
+ * @hide
+ */
+ int TRANSIT_KEYGUARD_UNOCCLUDE = 23;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "TRANSIT_" }, value = {
+ TRANSIT_UNSET,
+ TRANSIT_NONE,
+ TRANSIT_ACTIVITY_OPEN,
+ TRANSIT_ACTIVITY_CLOSE,
+ TRANSIT_TASK_OPEN,
+ TRANSIT_TASK_CLOSE,
+ TRANSIT_TASK_TO_FRONT,
+ TRANSIT_TASK_TO_BACK,
+ TRANSIT_WALLPAPER_CLOSE,
+ TRANSIT_WALLPAPER_OPEN,
+ TRANSIT_WALLPAPER_INTRA_OPEN,
+ TRANSIT_WALLPAPER_INTRA_CLOSE,
+ TRANSIT_TASK_OPEN_BEHIND,
+ TRANSIT_TASK_IN_PLACE,
+ TRANSIT_ACTIVITY_RELAUNCH,
+ TRANSIT_DOCK_TASK_FROM_RECENTS,
+ TRANSIT_KEYGUARD_GOING_AWAY,
+ TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+ TRANSIT_KEYGUARD_OCCLUDE,
+ TRANSIT_KEYGUARD_UNOCCLUDE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface TransitionType {}
+
+ /**
+ * Transition flag: Keyguard is going away, but keeping the notification shade open
+ * @hide
+ */
+ int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE = 0x1;
+
+ /**
+ * Transition flag: Keyguard is going away, but doesn't want an animation for it
+ * @hide
+ */
+ int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION = 0x2;
+
+ /**
+ * Transition flag: Keyguard is going away while it was showing the system wallpaper.
+ * @hide
+ */
+ int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER = 0x4;
+
+ /**
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = {
+ TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE,
+ TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION,
+ TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface TransitionFlags {}
+
+ /**
* Exception that is thrown when trying to add view whose
* {@link LayoutParams} {@link LayoutParams#token}
* is invalid.
@@ -889,7 +1072,12 @@
* decorations around the border (such as the status bar). The
* window must correctly position its contents to take the screen
* decoration into account. This flag is normally set for you
- * by Window as described in {@link Window#setFlags}. */
+ * by Window as described in {@link Window#setFlags}.
+ *
+ * <p>Note: on displays that have a {@link DisplayCutout}, the window may be placed
+ * such that it avoids the {@link DisplayCutout} area if necessary according to the
+ * {@link #layoutInDisplayCutoutMode}.
+ */
public static final int FLAG_LAYOUT_IN_SCREEN = 0x00000100;
/** Window flag: allow window to extend outside of the screen. */
@@ -1295,33 +1483,6 @@
}, formatToHexString = true)
public int flags;
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @LongDef(
- flag = true,
- value = {
- LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA,
- })
- @interface Flags2 {}
-
- /**
- * Window flag: allow placing the window within the area that overlaps with the
- * display cutout.
- *
- * <p>
- * The window must correctly position its contents to take the display cutout into account.
- *
- * @see DisplayCutout
- */
- public static final long FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA = 0x00000001;
-
- /**
- * Various behavioral options/flags. Default is none.
- *
- * @see #FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA
- */
- @Flags2 public long flags2;
-
/**
* If the window has requested hardware acceleration, but this is not
* allowed in the process it is in, then still render it as if it is
@@ -2050,6 +2211,77 @@
*/
public boolean hasSystemUiListeners;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ flag = true,
+ value = {LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT,
+ LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS,
+ LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER})
+ @interface LayoutInDisplayCutoutMode {}
+
+ /**
+ * Controls how the window is laid out if there is a {@link DisplayCutout}.
+ *
+ * <p>
+ * Defaults to {@link #LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT}.
+ *
+ * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
+ * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
+ * @see DisplayCutout
+ */
+ @LayoutInDisplayCutoutMode
+ public int layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
+
+ /**
+ * The window is allowed to extend into the {@link DisplayCutout} area, only if the
+ * {@link DisplayCutout} is fully contained within the status bar. Otherwise, the window is
+ * laid out such that it does not overlap with the {@link DisplayCutout} area.
+ *
+ * <p>
+ * In practice, this means that if the window did not set FLAG_FULLSCREEN or
+ * SYSTEM_UI_FLAG_FULLSCREEN, it can extend into the cutout area in portrait.
+ * Otherwise (i.e. fullscreen or landscape) it is laid out such that it does overlap the
+ * cutout area.
+ *
+ * <p>
+ * The usual precautions for not overlapping with the status bar are sufficient for ensuring
+ * that no important content overlaps with the DisplayCutout.
+ *
+ * @see DisplayCutout
+ * @see WindowInsets
+ */
+ public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT = 0;
+
+ /**
+ * The window is always allowed to extend into the {@link DisplayCutout} area,
+ * even if fullscreen or in landscape.
+ *
+ * <p>
+ * The window must make sure that no important content overlaps with the
+ * {@link DisplayCutout}.
+ *
+ * @see DisplayCutout
+ * @see WindowInsets#getDisplayCutout()
+ */
+ public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS = 1;
+
+ /**
+ * The window is never allowed to overlap with the DisplayCutout area.
+ *
+ * <p>
+ * This should be used with windows that transiently set SYSTEM_UI_FLAG_FULLSCREEN to
+ * avoid a relayout of the window when the flag is set or cleared.
+ *
+ * @see DisplayCutout
+ * @see View#SYSTEM_UI_FLAG_FULLSCREEN SYSTEM_UI_FLAG_FULLSCREEN
+ * @see View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ */
+ public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER = 2;
+
+
/**
* When this window has focus, disable touch pad pointer gesture processing.
* The window will receive raw position updates from the touch pad instead
@@ -2273,9 +2505,9 @@
out.writeInt(y);
out.writeInt(type);
out.writeInt(flags);
- out.writeLong(flags2);
out.writeInt(privateFlags);
out.writeInt(softInputMode);
+ out.writeInt(layoutInDisplayCutoutMode);
out.writeInt(gravity);
out.writeFloat(horizontalMargin);
out.writeFloat(verticalMargin);
@@ -2329,9 +2561,9 @@
y = in.readInt();
type = in.readInt();
flags = in.readInt();
- flags2 = in.readLong();
privateFlags = in.readInt();
softInputMode = in.readInt();
+ layoutInDisplayCutoutMode = in.readInt();
gravity = in.readInt();
horizontalMargin = in.readFloat();
verticalMargin = in.readFloat();
@@ -2462,10 +2694,6 @@
flags = o.flags;
changes |= FLAGS_CHANGED;
}
- if (flags2 != o.flags2) {
- flags2 = o.flags2;
- changes |= FLAGS_CHANGED;
- }
if (privateFlags != o.privateFlags) {
privateFlags = o.privateFlags;
changes |= PRIVATE_FLAGS_CHANGED;
@@ -2474,6 +2702,10 @@
softInputMode = o.softInputMode;
changes |= SOFT_INPUT_MODE_CHANGED;
}
+ if (layoutInDisplayCutoutMode != o.layoutInDisplayCutoutMode) {
+ layoutInDisplayCutoutMode = o.layoutInDisplayCutoutMode;
+ changes |= LAYOUT_CHANGED;
+ }
if (gravity != o.gravity) {
gravity = o.gravity;
changes |= LAYOUT_CHANGED;
@@ -2651,6 +2883,10 @@
sb.append(softInputModeToString(softInputMode));
sb.append('}');
}
+ if (layoutInDisplayCutoutMode != 0) {
+ sb.append(" layoutInDisplayCutoutMode=");
+ sb.append(layoutInDisplayCutoutModeToString(layoutInDisplayCutoutMode));
+ }
sb.append(" ty=");
sb.append(ViewDebug.intToString(LayoutParams.class, "type", type));
if (format != PixelFormat.OPAQUE) {
@@ -2719,11 +2955,6 @@
sb.append(System.lineSeparator());
sb.append(prefix).append(" fl=").append(
ViewDebug.flagsToString(LayoutParams.class, "flags", flags));
- if (flags2 != 0) {
- sb.append(System.lineSeparator());
- // TODO(roosa): add a long overload for ViewDebug.flagsToString.
- sb.append(prefix).append(" fl2=0x").append(Long.toHexString(flags2));
- }
if (privateFlags != 0) {
sb.append(System.lineSeparator());
sb.append(prefix).append(" pfl=").append(ViewDebug.flagsToString(
@@ -2771,7 +3002,6 @@
proto.write(NEEDS_MENU_KEY, needsMenuKey);
proto.write(COLOR_MODE, mColorMode);
proto.write(FLAGS, flags);
- proto.write(FLAGS_EXTRA, flags2);
proto.write(PRIVATE_FLAGS, privateFlags);
proto.write(SYSTEM_UI_VISIBILITY_FLAGS, systemUiVisibility);
proto.write(SUBTREE_SYSTEM_UI_VISIBILITY_FLAGS, subtreeSystemUiVisibility);
@@ -2849,6 +3079,20 @@
&& height == WindowManager.LayoutParams.MATCH_PARENT;
}
+ private static String layoutInDisplayCutoutModeToString(
+ @LayoutInDisplayCutoutMode int mode) {
+ switch (mode) {
+ case LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:
+ return "default";
+ case LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS:
+ return "always";
+ case LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:
+ return "never";
+ default:
+ return "unknown(" + mode + ")";
+ }
+ }
+
private static String softInputModeToString(@SoftInputModeFlags int softInputMode) {
final StringBuilder result = new StringBuilder();
final int state = softInputMode & SOFT_INPUT_MASK_STATE;
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index 9c1c9e3..a6f36bb 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -45,6 +45,11 @@
int PRESENCE_INTERNAL = 1 << 0;
int PRESENCE_EXTERNAL = 1 << 1;
+ // Navigation bar position values
+ int NAV_BAR_LEFT = 1 << 0;
+ int NAV_BAR_RIGHT = 1 << 1;
+ int NAV_BAR_BOTTOM = 1 << 2;
+
/**
* Sticky broadcast of the current HDMI plugged state.
*/
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 6c2d349..e0f74a7 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -192,9 +192,11 @@
* <b>TRANSITION TYPES</b></br>
* </p>
* <p>
- * <b>Window state changed</b> - represents the event of opening a
- * {@link android.widget.PopupWindow}, {@link android.view.Menu},
- * {@link android.app.Dialog}, etc.</br>
+ * <b>Window state changed</b> - represents the event of a change to a section of
+ * the user interface that is visually distinct. Should be sent from either the
+ * root view of a window or from a view that is marked as a pane
+ * {@link android.view.View#setAccessibilityPaneTitle(CharSequence)}. Not that changes
+ * to true windows are represented by {@link #TYPE_WINDOWS_CHANGED}.</br>
* <em>Type:</em> {@link #TYPE_WINDOW_STATE_CHANGED}</br>
* <em>Properties:</em></br>
* <ul>
@@ -203,7 +205,7 @@
* <li>{@link #getClassName()} - The class name of the source.</li>
* <li>{@link #getPackageName()} - The package name of the source.</li>
* <li>{@link #getEventTime()} - The event time.</li>
- * <li>{@link #getText()} - The text of the source's sub-tree.</li>
+ * <li>{@link #getText()} - The text of the source's sub-tree, including the pane titles.</li>
* </ul>
* </p>
* <p>
@@ -436,8 +438,10 @@
public static final int TYPE_VIEW_TEXT_CHANGED = 0x00000010;
/**
- * Represents the event of opening a {@link android.widget.PopupWindow},
- * {@link android.view.Menu}, {@link android.app.Dialog}, etc.
+ * Represents the event of a change to a visually distinct section of the user interface.
+ * These events should only be dispatched from {@link android.view.View}s that have
+ * accessibility pane titles, and replaces {@link #TYPE_WINDOW_CONTENT_CHANGED} for those
+ * sources. Details about the change are available from {@link #getContentChangeTypes()}.
*/
public static final int TYPE_WINDOW_STATE_CHANGED = 0x00000020;
@@ -565,6 +569,30 @@
public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0x00000004;
/**
+ * Change type for {@link #TYPE_WINDOW_STATE_CHANGED} event:
+ * The node's pane title changed.
+ */
+ public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 0x00000008;
+
+ /**
+ * Change type for {@link #TYPE_WINDOW_STATE_CHANGED} event:
+ * The node has a pane title, and either just appeared or just was assigned a title when it
+ * had none before.
+ */
+ public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 0x00000010;
+
+ /**
+ * Change type for {@link #TYPE_WINDOW_STATE_CHANGED} event:
+ * Can mean one of two slightly different things. The primary meaning is that the node has
+ * a pane title, and was removed from the node hierarchy. It will also be sent if the pane
+ * title is set to {@code null} after it contained a title.
+ * No source will be returned if the node is no longer on the screen. To make the change more
+ * clear for the user, the first entry in {@link #getText()} will return the value that would
+ * have been returned by {@code getSource().getPaneTitle()}.
+ */
+ public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 0x00000020;
+
+ /**
* Change type for {@link #TYPE_WINDOWS_CHANGED} event:
* The window was added.
*/
@@ -654,7 +682,8 @@
CONTENT_CHANGE_TYPE_UNDEFINED,
CONTENT_CHANGE_TYPE_SUBTREE,
CONTENT_CHANGE_TYPE_TEXT,
- CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION
+ CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION,
+ CONTENT_CHANGE_TYPE_PANE_TITLE
})
public @interface ContentChangeTypes {}
@@ -1244,43 +1273,33 @@
builder.append("EventType: ").append(eventTypeToString(mEventType));
builder.append("; EventTime: ").append(mEventTime);
builder.append("; PackageName: ").append(mPackageName);
- builder.append("; MovementGranularity: ").append(mMovementGranularity);
- builder.append("; Action: ").append(mAction);
- builder.append("; ContentChangeTypes: ").append(
- contentChangeTypesToString(mContentChangeTypes));
- builder.append("; WindowChangeTypes: ").append(
- windowChangeTypesToString(mWindowChangeTypes));
- builder.append(super.toString());
- if (DEBUG) {
- builder.append("\n");
- builder.append("; sourceWindowId: ").append(mSourceWindowId);
- builder.append("; mSourceNodeId: ").append(mSourceNodeId);
- for (int i = 0; i < getRecordCount(); i++) {
- final AccessibilityRecord record = getRecord(i);
- builder.append(" Record ");
- builder.append(i);
- builder.append(":");
- builder.append(" [ ClassName: " + record.mClassName);
- builder.append("; Text: " + record.mText);
- builder.append("; ContentDescription: " + record.mContentDescription);
- builder.append("; ItemCount: " + record.mItemCount);
- builder.append("; CurrentItemIndex: " + record.mCurrentItemIndex);
- builder.append("; IsEnabled: " + record.isEnabled());
- builder.append("; IsPassword: " + record.isPassword());
- builder.append("; IsChecked: " + record.isChecked());
- builder.append("; IsFullScreen: " + record.isFullScreen());
- builder.append("; Scrollable: " + record.isScrollable());
- builder.append("; BeforeText: " + record.mBeforeText);
- builder.append("; FromIndex: " + record.mFromIndex);
- builder.append("; ToIndex: " + record.mToIndex);
- builder.append("; ScrollX: " + record.mScrollX);
- builder.append("; ScrollY: " + record.mScrollY);
- builder.append("; AddedCount: " + record.mAddedCount);
- builder.append("; RemovedCount: " + record.mRemovedCount);
- builder.append("; ParcelableData: " + record.mParcelableData);
- builder.append(" ]");
+ if (!DEBUG_CONCISE_TOSTRING || mMovementGranularity != 0) {
+ builder.append("; MovementGranularity: ").append(mMovementGranularity);
+ }
+ if (!DEBUG_CONCISE_TOSTRING || mAction != 0) {
+ builder.append("; Action: ").append(mAction);
+ }
+ if (!DEBUG_CONCISE_TOSTRING || mContentChangeTypes != 0) {
+ builder.append("; ContentChangeTypes: ").append(
+ contentChangeTypesToString(mContentChangeTypes));
+ }
+ if (!DEBUG_CONCISE_TOSTRING || mWindowChangeTypes != 0) {
+ builder.append("; WindowChangeTypes: ").append(
+ contentChangeTypesToString(mWindowChangeTypes));
+ }
+ super.appendTo(builder);
+ if (DEBUG || DEBUG_CONCISE_TOSTRING) {
+ if (!DEBUG_CONCISE_TOSTRING) {
builder.append("\n");
}
+ if (DEBUG) {
+ builder.append("; SourceWindowId: ").append(mSourceWindowId);
+ builder.append("; SourceNodeId: ").append(mSourceNodeId);
+ }
+ for (int i = 0; i < getRecordCount(); i++) {
+ builder.append(" Record ").append(i).append(":");
+ getRecord(i).appendTo(builder).append("\n");
+ }
} else {
builder.append("; recordCount: ").append(getRecordCount());
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 28ef697..311dd4b 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -723,6 +723,7 @@
private CharSequence mText;
private CharSequence mHintText;
private CharSequence mError;
+ private CharSequence mPaneTitle;
private CharSequence mContentDescription;
private String mViewIdResourceName;
private ArrayList<String> mExtraDataKeys;
@@ -2033,6 +2034,33 @@
}
/**
+ * If this node represents a visually distinct region of the screen that may update separately
+ * from the rest of the window, it is considered a pane. Set the pane title to indicate that
+ * the node is a pane, and to provide a title for it.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ * @param paneTitle The title of the pane represented by this node.
+ */
+ public void setPaneTitle(@Nullable CharSequence paneTitle) {
+ enforceNotSealed();
+ mPaneTitle = (paneTitle == null)
+ ? null : paneTitle.subSequence(0, paneTitle.length());
+ }
+
+ /**
+ * Get the title of the pane represented by this node.
+ *
+ * @return The title of the pane represented by this node, or {@code null} if this node does
+ * not represent a pane.
+ */
+ public @Nullable CharSequence getPaneTitle() {
+ return mPaneTitle;
+ }
+
+ /**
* Get the drawing order of the view corresponding it this node.
* <p>
* Drawing order is determined only within the node's parent, so this index is only relative
@@ -3151,6 +3179,10 @@
nonDefaultFields |= bitAt(fieldIndex);
}
fieldIndex++;
+ if (!Objects.equals(mPaneTitle, DEFAULT.mPaneTitle)) {
+ nonDefaultFields |= bitAt(fieldIndex);
+ }
+ fieldIndex++;
if (!Objects.equals(mViewIdResourceName, DEFAULT.mViewIdResourceName)) {
nonDefaultFields |= bitAt(fieldIndex);
}
@@ -3270,6 +3302,7 @@
if (isBitSet(nonDefaultFields, fieldIndex++)) {
parcel.writeCharSequence(mContentDescription);
}
+ if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mPaneTitle);
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeString(mViewIdResourceName);
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mTextSelectionStart);
@@ -3341,6 +3374,7 @@
mHintText = other.mHintText;
mError = other.mError;
mContentDescription = other.mContentDescription;
+ mPaneTitle = other.mPaneTitle;
mViewIdResourceName = other.mViewIdResourceName;
if (mActions != null) mActions.clear();
@@ -3461,6 +3495,7 @@
if (isBitSet(nonDefaultFields, fieldIndex++)) {
mContentDescription = parcel.readCharSequence();
}
+ if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readString();
if (isBitSet(nonDefaultFields, fieldIndex++)) mViewIdResourceName = parcel.readString();
if (isBitSet(nonDefaultFields, fieldIndex++)) mTextSelectionStart = parcel.readInt();
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index fa505c9..0a709f8 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -16,6 +16,8 @@
package android.view.accessibility;
+import static com.android.internal.util.CollectionUtils.isEmpty;
+
import android.annotation.Nullable;
import android.os.Parcelable;
import android.view.View;
@@ -55,6 +57,8 @@
* @see AccessibilityNodeInfo
*/
public class AccessibilityRecord {
+ /** @hide */
+ protected static final boolean DEBUG_CONCISE_TOSTRING = false;
private static final int UNDEFINED = -1;
@@ -888,28 +892,69 @@
@Override
public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append(" [ ClassName: " + mClassName);
- builder.append("; Text: " + mText);
- builder.append("; ContentDescription: " + mContentDescription);
- builder.append("; ItemCount: " + mItemCount);
- builder.append("; CurrentItemIndex: " + mCurrentItemIndex);
- builder.append("; IsEnabled: " + getBooleanProperty(PROPERTY_ENABLED));
- builder.append("; IsPassword: " + getBooleanProperty(PROPERTY_PASSWORD));
- builder.append("; IsChecked: " + getBooleanProperty(PROPERTY_CHECKED));
- builder.append("; IsFullScreen: " + getBooleanProperty(PROPERTY_FULL_SCREEN));
- builder.append("; Scrollable: " + getBooleanProperty(PROPERTY_SCROLLABLE));
- builder.append("; BeforeText: " + mBeforeText);
- builder.append("; FromIndex: " + mFromIndex);
- builder.append("; ToIndex: " + mToIndex);
- builder.append("; ScrollX: " + mScrollX);
- builder.append("; ScrollY: " + mScrollY);
- builder.append("; MaxScrollX: " + mMaxScrollX);
- builder.append("; MaxScrollY: " + mMaxScrollY);
- builder.append("; AddedCount: " + mAddedCount);
- builder.append("; RemovedCount: " + mRemovedCount);
- builder.append("; ParcelableData: " + mParcelableData);
+ return appendTo(new StringBuilder()).toString();
+ }
+
+ StringBuilder appendTo(StringBuilder builder) {
+ builder.append(" [ ClassName: ").append(mClassName);
+ if (!DEBUG_CONCISE_TOSTRING || !isEmpty(mText)) {
+ appendPropName(builder, "Text").append(mText);
+ }
+ append(builder, "ContentDescription", mContentDescription);
+ append(builder, "ItemCount", mItemCount);
+ append(builder, "CurrentItemIndex", mCurrentItemIndex);
+
+ appendUnless(true, PROPERTY_ENABLED, builder);
+ appendUnless(false, PROPERTY_PASSWORD, builder);
+ appendUnless(false, PROPERTY_CHECKED, builder);
+ appendUnless(false, PROPERTY_FULL_SCREEN, builder);
+ appendUnless(false, PROPERTY_SCROLLABLE, builder);
+
+ append(builder, "BeforeText", mBeforeText);
+ append(builder, "FromIndex", mFromIndex);
+ append(builder, "ToIndex", mToIndex);
+ append(builder, "ScrollX", mScrollX);
+ append(builder, "ScrollY", mScrollY);
+ append(builder, "MaxScrollX", mMaxScrollX);
+ append(builder, "MaxScrollY", mMaxScrollY);
+ append(builder, "AddedCount", mAddedCount);
+ append(builder, "RemovedCount", mRemovedCount);
+ append(builder, "ParcelableData", mParcelableData);
builder.append(" ]");
- return builder.toString();
+ return builder;
+ }
+
+ private void appendUnless(boolean defValue, int prop, StringBuilder builder) {
+ boolean value = getBooleanProperty(prop);
+ if (DEBUG_CONCISE_TOSTRING && value == defValue) return;
+ appendPropName(builder, singleBooleanPropertyToString(prop))
+ .append(value);
+ }
+
+ private static String singleBooleanPropertyToString(int prop) {
+ switch (prop) {
+ case PROPERTY_CHECKED: return "Checked";
+ case PROPERTY_ENABLED: return "Enabled";
+ case PROPERTY_PASSWORD: return "Password";
+ case PROPERTY_FULL_SCREEN: return "FullScreen";
+ case PROPERTY_SCROLLABLE: return "Scrollable";
+ case PROPERTY_IMPORTANT_FOR_ACCESSIBILITY:
+ return "ImportantForAccessibility";
+ default: return Integer.toHexString(prop);
+ }
+ }
+
+ private void append(StringBuilder builder, String propName, int propValue) {
+ if (DEBUG_CONCISE_TOSTRING && propValue == UNDEFINED) return;
+ appendPropName(builder, propName).append(propValue);
+ }
+
+ private void append(StringBuilder builder, String propName, Object propValue) {
+ if (DEBUG_CONCISE_TOSTRING && propValue == null) return;
+ appendPropName(builder, propName).append(propValue);
+ }
+
+ private StringBuilder appendPropName(StringBuilder builder, String propName) {
+ return builder.append("; ").append(propName).append(": ");
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityViewHierarchyState.java b/core/java/android/view/accessibility/AccessibilityViewHierarchyState.java
new file mode 100644
index 0000000..447fafa
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityViewHierarchyState.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 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.accessibility;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * Accessibility-related state of a {@link android.view.ViewRootImpl}
+ *
+ * @hide
+ */
+public class AccessibilityViewHierarchyState {
+ private @Nullable SendViewScrolledAccessibilityEvent mSendViewScrolledAccessibilityEvent;
+ private @Nullable SendWindowContentChangedAccessibilityEvent
+ mSendWindowContentChangedAccessibilityEvent;
+
+ /**
+ * @return a {@link SendViewScrolledAccessibilityEvent}, creating one if needed
+ */
+ public @NonNull SendViewScrolledAccessibilityEvent getSendViewScrolledAccessibilityEvent() {
+ if (mSendViewScrolledAccessibilityEvent == null) {
+ mSendViewScrolledAccessibilityEvent = new SendViewScrolledAccessibilityEvent();
+ }
+ return mSendViewScrolledAccessibilityEvent;
+ }
+
+ public boolean isScrollEventSenderInitialized() {
+ return mSendViewScrolledAccessibilityEvent != null;
+ }
+
+ /**
+ * @return a {@link SendWindowContentChangedAccessibilityEvent}, creating one if needed
+ */
+ public @NonNull SendWindowContentChangedAccessibilityEvent
+ getSendWindowContentChangedAccessibilityEvent() {
+ if (mSendWindowContentChangedAccessibilityEvent == null) {
+ mSendWindowContentChangedAccessibilityEvent =
+ new SendWindowContentChangedAccessibilityEvent();
+ }
+ return mSendWindowContentChangedAccessibilityEvent;
+ }
+
+ public boolean isWindowContentChangedEventSenderInitialized() {
+ return mSendWindowContentChangedAccessibilityEvent != null;
+ }
+}
diff --git a/core/java/android/view/accessibility/SendViewScrolledAccessibilityEvent.java b/core/java/android/view/accessibility/SendViewScrolledAccessibilityEvent.java
new file mode 100644
index 0000000..40a1b6a
--- /dev/null
+++ b/core/java/android/view/accessibility/SendViewScrolledAccessibilityEvent.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 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.accessibility;
+
+
+import android.annotation.NonNull;
+import android.view.View;
+
+/**
+ * Sender for {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} accessibility event.
+ *
+ * @hide
+ */
+public class SendViewScrolledAccessibilityEvent extends ThrottlingAccessibilityEventSender {
+
+ public int mDeltaX;
+ public int mDeltaY;
+
+ /**
+ * Post a scroll event to be sent for the given view
+ */
+ public void post(View source, int dx, int dy) {
+ if (!isPendingFor(source)) sendNowIfPending();
+
+ mDeltaX += dx;
+ mDeltaY += dy;
+
+ if (!isPendingFor(source)) scheduleFor(source);
+ }
+
+ @Override
+ protected void performSendEvent(@NonNull View source) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED);
+ event.setScrollDeltaX(mDeltaX);
+ event.setScrollDeltaY(mDeltaY);
+ source.sendAccessibilityEventUnchecked(event);
+ }
+
+ @Override
+ protected void resetState(@NonNull View source) {
+ mDeltaX = 0;
+ mDeltaY = 0;
+ }
+}
diff --git a/core/java/android/view/accessibility/SendWindowContentChangedAccessibilityEvent.java b/core/java/android/view/accessibility/SendWindowContentChangedAccessibilityEvent.java
new file mode 100644
index 0000000..df38fba
--- /dev/null
+++ b/core/java/android/view/accessibility/SendWindowContentChangedAccessibilityEvent.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 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.accessibility;
+
+
+import static com.android.internal.util.ObjectUtils.firstNotNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.View;
+import android.view.ViewParent;
+
+import java.util.HashSet;
+
+/**
+ * @hide
+ */
+public class SendWindowContentChangedAccessibilityEvent
+ extends ThrottlingAccessibilityEventSender {
+
+ private int mChangeTypes = 0;
+
+ private HashSet<View> mTempHashSet;
+
+ @Override
+ protected void performSendEvent(@NonNull View source) {
+ AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ event.setContentChangeTypes(mChangeTypes);
+ source.sendAccessibilityEventUnchecked(event);
+ }
+
+ @Override
+ protected void resetState(@Nullable View source) {
+ if (source != null) {
+ source.resetSubtreeAccessibilityStateChanged();
+ }
+ mChangeTypes = 0;
+ }
+
+ /**
+ * Post the {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event with the given
+ * {@link AccessibilityEvent#getContentChangeTypes change type} for the given view
+ */
+ public void runOrPost(View source, int changeType) {
+ if (source.getAccessibilityLiveRegion() != View.ACCESSIBILITY_LIVE_REGION_NONE) {
+ sendNowIfPending();
+ mChangeTypes = changeType;
+ sendNow(source);
+ } else {
+ mChangeTypes |= changeType;
+ scheduleFor(source);
+ }
+ }
+
+ @Override
+ protected @Nullable View tryMerge(@NonNull View oldSource, @NonNull View newSource) {
+ // If there is no common predecessor, then oldSource points to
+ // a removed view, hence in this case always prefer the newSource.
+ return firstNotNull(
+ getCommonPredecessor(oldSource, newSource),
+ newSource);
+ }
+
+ private View getCommonPredecessor(View first, View second) {
+ if (mTempHashSet == null) {
+ mTempHashSet = new HashSet<>();
+ }
+ HashSet<View> seen = mTempHashSet;
+ seen.clear();
+ View firstCurrent = first;
+ while (firstCurrent != null) {
+ seen.add(firstCurrent);
+ ViewParent firstCurrentParent = firstCurrent.getParent();
+ if (firstCurrentParent instanceof View) {
+ firstCurrent = (View) firstCurrentParent;
+ } else {
+ firstCurrent = null;
+ }
+ }
+ View secondCurrent = second;
+ while (secondCurrent != null) {
+ if (seen.contains(secondCurrent)) {
+ seen.clear();
+ return secondCurrent;
+ }
+ ViewParent secondCurrentParent = secondCurrent.getParent();
+ if (secondCurrentParent instanceof View) {
+ secondCurrent = (View) secondCurrentParent;
+ } else {
+ secondCurrent = null;
+ }
+ }
+ seen.clear();
+ return null;
+ }
+}
diff --git a/core/java/android/view/accessibility/ThrottlingAccessibilityEventSender.java b/core/java/android/view/accessibility/ThrottlingAccessibilityEventSender.java
new file mode 100644
index 0000000..66fa301
--- /dev/null
+++ b/core/java/android/view/accessibility/ThrottlingAccessibilityEventSender.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2017 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.accessibility;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewRootImpl;
+import android.view.ViewRootImpl.CalledFromWrongThreadException;
+
+/**
+ * A throttling {@link AccessibilityEvent} sender that relies on its currently associated
+ * 'source' view's {@link View#postDelayed delayed execution} to delay and possibly
+ * {@link #tryMerge merge} together any events that come in less than
+ * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval
+ * the configured amount of milliseconds} apart.
+ *
+ * The suggested usage is to create a singleton extending this class, holding any state specific to
+ * the particular event type that the subclass represents, and have an 'entrypoint' method that
+ * delegates to {@link #scheduleFor(View)}.
+ * For example:
+ *
+ * {@code
+ * public void post(View view, String text, int resId) {
+ * mText = text;
+ * mId = resId;
+ * scheduleFor(view);
+ * }
+ * }
+ *
+ * @see #scheduleFor(View)
+ * @see #tryMerge(View, View)
+ * @see #performSendEvent(View)
+ * @hide
+ */
+public abstract class ThrottlingAccessibilityEventSender {
+
+ private static final boolean DEBUG = false;
+ private static final String LOG_TAG = "ThrottlingA11ySender";
+
+ View mSource;
+ private long mLastSendTimeMillis = Long.MIN_VALUE;
+ private boolean mIsPending = false;
+
+ private final Runnable mWorker = () -> {
+ View source = mSource;
+ if (DEBUG) Log.d(LOG_TAG, thisClass() + ".run(mSource = " + source + ")");
+
+ if (!checkAndResetIsPending() || source == null) {
+ resetStateInternal();
+ return;
+ }
+
+ // Accessibility may be turned off while we were waiting
+ if (isAccessibilityEnabled(source)) {
+ mLastSendTimeMillis = SystemClock.uptimeMillis();
+ performSendEvent(source);
+ }
+ resetStateInternal();
+ };
+
+ /**
+ * Populate and send an {@link AccessibilityEvent} using the given {@code source} view, as well
+ * as any extra data from this instance's state.
+ *
+ * Send the event via {@link View#sendAccessibilityEventUnchecked(AccessibilityEvent)} or
+ * {@link View#sendAccessibilityEvent(int)} on the provided {@code source} view to allow for
+ * overrides of those methods on {@link View} subclasses to take effect, and/or make sure that
+ * an {@link View#getAccessibilityDelegate() accessibility delegate} is not ignored if any.
+ */
+ protected abstract void performSendEvent(@NonNull View source);
+
+ /**
+ * Perform optional cleanup after {@link #performSendEvent}
+ *
+ * @param source the view this event was associated with
+ */
+ protected abstract void resetState(@Nullable View source);
+
+ /**
+ * Attempt to merge the pending events for source views {@code oldSource} and {@code newSource}
+ * into one, with source set to the resulting {@link View}
+ *
+ * A result of {@code null} means merger is not possible, resulting in the currently pending
+ * event being flushed before proceeding.
+ */
+ protected @Nullable View tryMerge(@NonNull View oldSource, @NonNull View newSource) {
+ return null;
+ }
+
+ /**
+ * Schedules a {@link #performSendEvent} with the source {@link View} set to given
+ * {@code source}
+ *
+ * If an event is already scheduled a {@link #tryMerge merge} will be attempted.
+ * If merging is not possible (as indicated by the null result from {@link #tryMerge}),
+ * the currently scheduled event will be {@link #sendNow sent immediately} and the new one
+ * will be scheduled afterwards.
+ */
+ protected final void scheduleFor(@NonNull View source) {
+ if (DEBUG) Log.d(LOG_TAG, thisClass() + ".scheduleFor(source = " + source + ")");
+
+ Handler uiHandler = source.getHandler();
+ if (uiHandler == null || uiHandler.getLooper() != Looper.myLooper()) {
+ CalledFromWrongThreadException e = new CalledFromWrongThreadException(
+ "Expected to be called from main thread but was called from "
+ + Thread.currentThread());
+ // TODO: Throw the exception
+ Log.e(LOG_TAG, "Accessibility content change on non-UI thread. Future Android "
+ + "versions will throw an exception.", e);
+ }
+
+ if (!isAccessibilityEnabled(source)) return;
+
+ if (mIsPending) {
+ View merged = tryMerge(mSource, source);
+ if (merged != null) {
+ setSource(merged);
+ return;
+ } else {
+ sendNow();
+ }
+ }
+
+ setSource(source);
+
+ final long timeSinceLastMillis = SystemClock.uptimeMillis() - mLastSendTimeMillis;
+ final long minEventIntervalMillis =
+ ViewConfiguration.getSendRecurringAccessibilityEventsInterval();
+ if (timeSinceLastMillis >= minEventIntervalMillis) {
+ sendNow();
+ } else {
+ mSource.postDelayed(mWorker, minEventIntervalMillis - timeSinceLastMillis);
+ }
+ }
+
+ static boolean isAccessibilityEnabled(@NonNull View contextProvider) {
+ return AccessibilityManager.getInstance(contextProvider.getContext()).isEnabled();
+ }
+
+ protected final void sendNow(View source) {
+ setSource(source);
+ sendNow();
+ }
+
+ private void sendNow() {
+ mSource.removeCallbacks(mWorker);
+ mWorker.run();
+ }
+
+ /**
+ * Flush the event if one is pending
+ */
+ public void sendNowIfPending() {
+ if (mIsPending) sendNow();
+ }
+
+ /**
+ * Cancel the event if one is pending and is for the given view
+ */
+ public final void cancelIfPendingFor(@NonNull View source) {
+ if (isPendingFor(source)) cancelIfPending(this);
+ }
+
+ /**
+ * @return whether an event is currently pending for the given source view
+ */
+ protected final boolean isPendingFor(@Nullable View source) {
+ return mIsPending && mSource == source;
+ }
+
+ /**
+ * Cancel the event if one is not null and pending
+ */
+ public static void cancelIfPending(@Nullable ThrottlingAccessibilityEventSender sender) {
+ if (sender == null || !sender.checkAndResetIsPending()) return;
+ sender.mSource.removeCallbacks(sender.mWorker);
+ sender.resetStateInternal();
+ }
+
+ void resetStateInternal() {
+ if (DEBUG) Log.d(LOG_TAG, thisClass() + ".resetStateInternal()");
+
+ resetState(mSource);
+ setSource(null);
+ }
+
+ boolean checkAndResetIsPending() {
+ if (mIsPending) {
+ mIsPending = false;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private void setSource(@Nullable View source) {
+ if (DEBUG) Log.d(LOG_TAG, thisClass() + ".setSource(" + source + ")");
+
+ if (source == null && mIsPending) {
+ Log.e(LOG_TAG, "mSource nullified while callback still pending: " + this);
+ return;
+ }
+
+ if (source != null && !mIsPending) {
+ // At most one can be pending at any given time
+ View oldSource = mSource;
+ if (oldSource != null) {
+ ViewRootImpl viewRootImpl = oldSource.getViewRootImpl();
+ if (viewRootImpl != null) {
+ viewRootImpl.flushPendingAccessibilityEvents();
+ }
+ }
+ mIsPending = true;
+ }
+ mSource = source;
+ }
+
+ String thisClass() {
+ return getClass().getSimpleName();
+ }
+
+ @Override
+ public String toString() {
+ return thisClass() + "(" + mSource + ")";
+ }
+
+}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 78b41c6..de9b0d7 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -341,6 +341,10 @@
@GuardedBy("mLock")
@Nullable private AutofillId mSaveTriggerId;
+ /** set to true when onInvisibleForAutofill is called, used by onAuthenticationResult */
+ @GuardedBy("mLock")
+ private boolean mOnInvisibleCalled;
+
/** If set, session is commited when the activity is finished; otherwise session is canceled. */
@GuardedBy("mLock")
private boolean mSaveOnFinish;
@@ -397,6 +401,11 @@
boolean isVisibleForAutofill();
/**
+ * Client might disable enter/exit event e.g. when activity is paused.
+ */
+ boolean isDisablingEnterExitEventForAutofill();
+
+ /**
* Finds views by traversing the hierarchies of the client.
*
* @param viewIds The autofill ids of the views to find
@@ -499,6 +508,19 @@
}
/**
+ * Called once the client becomes invisible.
+ *
+ * @see AutofillClient#isVisibleForAutofill()
+ *
+ * {@hide}
+ */
+ public void onInvisibleForAutofill() {
+ synchronized (mLock) {
+ mOnInvisibleCalled = true;
+ }
+ }
+
+ /**
* Save state before activity lifecycle
*
* @param outState Place to store the state
@@ -623,21 +645,45 @@
return false;
}
+ private boolean isClientVisibleForAutofillLocked() {
+ final AutofillClient client = getClient();
+ return client != null && client.isVisibleForAutofill();
+ }
+
+ private boolean isClientDisablingEnterExitEvent() {
+ final AutofillClient client = getClient();
+ return client != null && client.isDisablingEnterExitEventForAutofill();
+ }
+
private void notifyViewEntered(@NonNull View view, int flags) {
if (!hasAutofillFeature()) {
return;
}
- AutofillCallback callback = null;
+ AutofillCallback callback;
synchronized (mLock) {
- if (shouldIgnoreViewEnteredLocked(view, flags)) return;
+ callback = notifyViewEnteredLocked(view, flags);
+ }
- ensureServiceClientAddedIfNeededLocked();
+ if (callback != null) {
+ mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
+ }
+ }
- if (!mEnabled) {
- if (mCallback != null) {
- callback = mCallback;
- }
- } else {
+ /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
+ private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
+ if (shouldIgnoreViewEnteredLocked(view, flags)) return null;
+
+ AutofillCallback callback = null;
+
+ ensureServiceClientAddedIfNeededLocked();
+
+ if (!mEnabled) {
+ if (mCallback != null) {
+ callback = mCallback;
+ }
+ } else {
+ // don't notify entered when Activity is already in background
+ if (!isClientDisablingEnterExitEvent()) {
final AutofillId id = getAutofillId(view);
final AutofillValue value = view.getAutofillValue();
@@ -650,10 +696,7 @@
}
}
}
-
- if (callback != null) {
- mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
- }
+ return callback;
}
/**
@@ -666,9 +709,16 @@
return;
}
synchronized (mLock) {
- ensureServiceClientAddedIfNeededLocked();
+ notifyViewExitedLocked(view);
+ }
+ }
- if (mEnabled && isActiveLocked()) {
+ void notifyViewExitedLocked(@NonNull View view) {
+ ensureServiceClientAddedIfNeededLocked();
+
+ if (mEnabled && isActiveLocked()) {
+ // dont notify exited when Activity is already in background
+ if (!isClientDisablingEnterExitEvent()) {
final AutofillId id = getAutofillId(view);
// Update focus on existing session.
@@ -719,7 +769,7 @@
}
}
if (mTrackedViews != null) {
- mTrackedViews.notifyViewVisibilityChanged(id, isVisible);
+ mTrackedViews.notifyViewVisibilityChangedLocked(id, isVisible);
}
}
}
@@ -752,17 +802,32 @@
if (!hasAutofillFeature()) {
return;
}
- AutofillCallback callback = null;
+ AutofillCallback callback;
synchronized (mLock) {
- if (shouldIgnoreViewEnteredLocked(view, flags)) return;
+ callback = notifyViewEnteredLocked(view, virtualId, bounds, flags);
+ }
- ensureServiceClientAddedIfNeededLocked();
+ if (callback != null) {
+ callback.onAutofillEvent(view, virtualId,
+ AutofillCallback.EVENT_INPUT_UNAVAILABLE);
+ }
+ }
- if (!mEnabled) {
- if (mCallback != null) {
- callback = mCallback;
- }
- } else {
+ /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
+ private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
+ int flags) {
+ AutofillCallback callback = null;
+ if (shouldIgnoreViewEnteredLocked(view, flags)) return callback;
+
+ ensureServiceClientAddedIfNeededLocked();
+
+ if (!mEnabled) {
+ if (mCallback != null) {
+ callback = mCallback;
+ }
+ } else {
+ // don't notify entered when Activity is already in background
+ if (!isClientDisablingEnterExitEvent()) {
final AutofillId id = getAutofillId(view, virtualId);
if (!isActiveLocked()) {
@@ -774,11 +839,7 @@
}
}
}
-
- if (callback != null) {
- callback.onAutofillEvent(view, virtualId,
- AutofillCallback.EVENT_INPUT_UNAVAILABLE);
- }
+ return callback;
}
/**
@@ -792,9 +853,16 @@
return;
}
synchronized (mLock) {
- ensureServiceClientAddedIfNeededLocked();
+ notifyViewExitedLocked(view, virtualId);
+ }
+ }
- if (mEnabled && isActiveLocked()) {
+ private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
+ ensureServiceClientAddedIfNeededLocked();
+
+ if (mEnabled && isActiveLocked()) {
+ // don't notify exited when Activity is already in background
+ if (!isClientDisablingEnterExitEvent()) {
final AutofillId id = getAutofillId(view, virtualId);
// Update focus on existing session.
@@ -1155,7 +1223,7 @@
}
/** @hide */
- public void onAuthenticationResult(int authenticationId, Intent data) {
+ public void onAuthenticationResult(int authenticationId, Intent data, View focusView) {
if (!hasAutofillFeature()) {
return;
}
@@ -1167,9 +1235,24 @@
if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
synchronized (mLock) {
- if (!isActiveLocked() || data == null) {
+ if (!isActiveLocked()) {
return;
}
+ // If authenticate activity closes itself during onCreate(), there is no onStop/onStart
+ // of app activity. We enforce enter event to re-show fill ui in such case.
+ // CTS example:
+ // LoginActivityTest#testDatasetAuthTwoFieldsUserCancelsFirstAttempt
+ // LoginActivityTest#testFillResponseAuthBothFieldsUserCancelsFirstAttempt
+ if (!mOnInvisibleCalled && focusView != null
+ && focusView.canNotifyAutofillEnterExitEvent()) {
+ notifyViewExitedLocked(focusView);
+ notifyViewEnteredLocked(focusView, 0);
+ }
+ if (data == null) {
+ // data is set to null when result is not RESULT_OK
+ return;
+ }
+
final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
final Bundle responseData = new Bundle();
responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
@@ -1402,6 +1485,9 @@
if (sessionId == mSessionId) {
final AutofillClient client = getClient();
if (client != null) {
+ // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
+ // before onAuthenticationResult()
+ mOnInvisibleCalled = false;
client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);
}
}
@@ -1767,6 +1853,7 @@
pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
+ pw.print(pfx); pw.print("onInvisibleCalled "); pw.println(mOnInvisibleCalled);
pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
pw.print(pfx); pw.print("tracked views: ");
if (mTrackedViews == null) {
@@ -1937,15 +2024,13 @@
* @param id the id of the view/virtual view whose visibility changed.
* @param isVisible visible if the view is visible in the view hierarchy.
*/
- void notifyViewVisibilityChanged(@NonNull AutofillId id, boolean isVisible) {
- AutofillClient client = getClient();
-
+ void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) {
if (sDebug) {
Log.d(TAG, "notifyViewVisibilityChanged(): id=" + id + " isVisible="
+ isVisible);
}
- if (client != null && client.isVisibleForAutofill()) {
+ if (isClientVisibleForAutofillLocked()) {
if (isVisible) {
if (isInSet(mInvisibleTrackedIds, id)) {
mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
diff --git a/core/java/android/view/autofill/AutofillPopupWindow.java b/core/java/android/view/autofill/AutofillPopupWindow.java
index 5cba21e..e80fdd9 100644
--- a/core/java/android/view/autofill/AutofillPopupWindow.java
+++ b/core/java/android/view/autofill/AutofillPopupWindow.java
@@ -78,8 +78,10 @@
public AutofillPopupWindow(@NonNull IAutofillWindowPresenter presenter) {
mWindowPresenter = new WindowPresenter(presenter);
+ setTouchModal(false);
setOutsideTouchable(true);
- setInputMethodMode(INPUT_METHOD_NEEDED);
+ setInputMethodMode(INPUT_METHOD_NOT_NEEDED);
+ setFocusable(true);
}
@Override
diff --git a/core/java/android/view/inputmethod/ExtractedText.java b/core/java/android/view/inputmethod/ExtractedText.java
index 003f221..1eb300e 100644
--- a/core/java/android/view/inputmethod/ExtractedText.java
+++ b/core/java/android/view/inputmethod/ExtractedText.java
@@ -29,6 +29,8 @@
public class ExtractedText implements Parcelable {
/**
* The text that has been extracted.
+ *
+ * @see android.widget.TextView#getText()
*/
public CharSequence text;
@@ -88,6 +90,8 @@
/**
* The hint that has been extracted.
+ *
+ * @see android.widget.TextView#getHint()
*/
public CharSequence hint;
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 57f9895..e554540 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -1,17 +1,17 @@
/*
- * Copyright (C) 2007-2008 The Android Open Source Project
+ * Copyright (C) 2007 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
+ * 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
+ * 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.
+ * 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.inputmethod;
@@ -131,13 +131,13 @@
* spans. <strong>Editor authors</strong>: you should strive to
* send text with styles if possible, but it is not required.
*/
- static final int GET_TEXT_WITH_STYLES = 0x0001;
+ int GET_TEXT_WITH_STYLES = 0x0001;
/**
* Flag for use with {@link #getExtractedText} to indicate you
* would like to receive updates when the extracted text changes.
*/
- public static final int GET_EXTRACTED_TEXT_MONITOR = 0x0001;
+ int GET_EXTRACTED_TEXT_MONITOR = 0x0001;
/**
* Get <var>n</var> characters of text before the current cursor
@@ -176,7 +176,7 @@
* @return the text before the cursor position; the length of the
* returned text might be less than <var>n</var>.
*/
- public CharSequence getTextBeforeCursor(int n, int flags);
+ CharSequence getTextBeforeCursor(int n, int flags);
/**
* Get <var>n</var> characters of text after the current cursor
@@ -215,7 +215,7 @@
* @return the text after the cursor position; the length of the
* returned text might be less than <var>n</var>.
*/
- public CharSequence getTextAfterCursor(int n, int flags);
+ CharSequence getTextAfterCursor(int n, int flags);
/**
* Gets the selected text, if any.
@@ -249,7 +249,7 @@
* later, returns false when the target application does not implement
* this method.
*/
- public CharSequence getSelectedText(int flags);
+ CharSequence getSelectedText(int flags);
/**
* Retrieve the current capitalization mode in effect at the
@@ -279,7 +279,7 @@
* @return the caps mode flags that are in effect at the current
* cursor position. See TYPE_TEXT_FLAG_CAPS_* in {@link android.text.InputType}.
*/
- public int getCursorCapsMode(int reqModes);
+ int getCursorCapsMode(int reqModes);
/**
* Retrieve the current text in the input connection's editor, and
@@ -314,8 +314,7 @@
* longer valid of the editor can't comply with the request for
* some reason.
*/
- public ExtractedText getExtractedText(ExtractedTextRequest request,
- int flags);
+ ExtractedText getExtractedText(ExtractedTextRequest request, int flags);
/**
* Delete <var>beforeLength</var> characters of text before the
@@ -342,8 +341,8 @@
* delete more characters than are in the editor, as that may have
* ill effects on the application. Calling this method will cause
* the editor to call
- * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int, int, int)}
- * on your service after the batch input is over.</p>
+ * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
+ * int, int)} on your service after the batch input is over.</p>
*
* <p><strong>Editor authors:</strong> please be careful of race
* conditions in implementing this call. An IME can make a change
@@ -369,7 +368,7 @@
* that range.
* @return true on success, false if the input connection is no longer valid.
*/
- public boolean deleteSurroundingText(int beforeLength, int afterLength);
+ boolean deleteSurroundingText(int beforeLength, int afterLength);
/**
* A variant of {@link #deleteSurroundingText(int, int)}. Major differences are:
@@ -397,7 +396,7 @@
* @return true on success, false if the input connection is no longer valid. Returns
* {@code false} when the target application does not implement this method.
*/
- public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength);
+ boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength);
/**
* Replace the currently composing text with the given text, and
@@ -416,8 +415,8 @@
* <p>This is usually called by IMEs to add or remove or change
* characters in the composing span. Calling this method will
* cause the editor to call
- * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int, int, int)}
- * on the current IME after the batch input is over.</p>
+ * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
+ * int, int)} on the current IME after the batch input is over.</p>
*
* <p><strong>Editor authors:</strong> please keep in mind the
* text may be very similar or completely different than what was
@@ -455,7 +454,7 @@
* @return true on success, false if the input connection is no longer
* valid.
*/
- public boolean setComposingText(CharSequence text, int newCursorPosition);
+ boolean setComposingText(CharSequence text, int newCursorPosition);
/**
* Mark a certain region of text as composing text. If there was a
@@ -474,8 +473,8 @@
* <p>Since this does not change the contents of the text, editors should not call
* {@link InputMethodManager#updateSelection(View, int, int, int, int)} and
* IMEs should not receive
- * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int, int, int)}.
- * </p>
+ * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
+ * int, int)}.</p>
*
* <p>This has no impact on the cursor/selection position. It may
* result in the cursor being anywhere inside or outside the
@@ -488,7 +487,7 @@
* valid. In {@link android.os.Build.VERSION_CODES#N} and later, false is returned when the
* target application does not implement this method.
*/
- public boolean setComposingRegion(int start, int end);
+ boolean setComposingRegion(int start, int end);
/**
* Have the text editor finish whatever composing text is
@@ -507,7 +506,7 @@
* @return true on success, false if the input connection
* is no longer valid.
*/
- public boolean finishComposingText();
+ boolean finishComposingText();
/**
* Commit text to the text box and set the new cursor position.
@@ -522,8 +521,8 @@
* then {@link #finishComposingText()}.</p>
*
* <p>Calling this method will cause the editor to call
- * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int, int, int)}
- * on the current IME after the batch input is over.
+ * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
+ * int, int)} on the current IME after the batch input is over.
* <strong>Editor authors</strong>, for this to happen you need to
* make the changes known to the input method by calling
* {@link InputMethodManager#updateSelection(View, int, int, int, int)},
@@ -543,7 +542,7 @@
* @return true on success, false if the input connection is no longer
* valid.
*/
- public boolean commitText(CharSequence text, int newCursorPosition);
+ boolean commitText(CharSequence text, int newCursorPosition);
/**
* Commit a completion the user has selected from the possible ones
@@ -569,8 +568,8 @@
*
* <p>Calling this method (with a valid {@link CompletionInfo} object)
* will cause the editor to call
- * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int, int, int)}
- * on the current IME after the batch input is over.
+ * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
+ * int, int)} on the current IME after the batch input is over.
* <strong>Editor authors</strong>, for this to happen you need to
* make the changes known to the input method by calling
* {@link InputMethodManager#updateSelection(View, int, int, int, int)},
@@ -581,15 +580,15 @@
* @return true on success, false if the input connection is no longer
* valid.
*/
- public boolean commitCompletion(CompletionInfo text);
+ boolean commitCompletion(CompletionInfo text);
/**
* Commit a correction automatically performed on the raw user's input. A
* typical example would be to correct typos using a dictionary.
*
* <p>Calling this method will cause the editor to call
- * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int, int, int)}
- * on the current IME after the batch input is over.
+ * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
+ * int, int)} on the current IME after the batch input is over.
* <strong>Editor authors</strong>, for this to happen you need to
* make the changes known to the input method by calling
* {@link InputMethodManager#updateSelection(View, int, int, int, int)},
@@ -601,7 +600,7 @@
* In {@link android.os.Build.VERSION_CODES#N} and later, returns false
* when the target application does not implement this method.
*/
- public boolean commitCorrection(CorrectionInfo correctionInfo);
+ boolean commitCorrection(CorrectionInfo correctionInfo);
/**
* Set the selection of the text editor. To set the cursor
@@ -609,8 +608,8 @@
*
* <p>Since this moves the cursor, calling this method will cause
* the editor to call
- * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int, int, int)}
- * on the current IME after the batch input is over.
+ * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
+ * int, int)} on the current IME after the batch input is over.
* <strong>Editor authors</strong>, for this to happen you need to
* make the changes known to the input method by calling
* {@link InputMethodManager#updateSelection(View, int, int, int, int)},
@@ -628,7 +627,7 @@
* @return true on success, false if the input connection is no longer
* valid.
*/
- public boolean setSelection(int start, int end);
+ boolean setSelection(int start, int end);
/**
* Have the editor perform an action it has said it can do.
@@ -642,7 +641,7 @@
* @return true on success, false if the input connection is no longer
* valid.
*/
- public boolean performEditorAction(int editorAction);
+ boolean performEditorAction(int editorAction);
/**
* Perform a context menu action on the field. The given id may be one of:
@@ -652,7 +651,7 @@
* {@link android.R.id#paste}, {@link android.R.id#copyUrl},
* or {@link android.R.id#switchInputMethod}
*/
- public boolean performContextMenuAction(int id);
+ boolean performContextMenuAction(int id);
/**
* Tell the editor that you are starting a batch of editor
@@ -662,8 +661,8 @@
*
* <p><strong>IME authors:</strong> use this to avoid getting
* calls to
- * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int, int, int)}
- * corresponding to intermediate state. Also, use this to avoid
+ * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
+ * int, int)} corresponding to intermediate state. Also, use this to avoid
* flickers that may arise from displaying intermediate state. Be
* sure to call {@link #endBatchEdit} for each call to this, or
* you may block updates in the editor.</p>
@@ -678,7 +677,7 @@
* this method starts a batch edit, that means it will always return true
* unless the input connection is no longer valid.
*/
- public boolean beginBatchEdit();
+ boolean beginBatchEdit();
/**
* Tell the editor that you are done with a batch edit previously
@@ -696,7 +695,7 @@
* the latest one (in other words, if the nesting count is > 0), false
* otherwise or if the input connection is no longer valid.
*/
- public boolean endBatchEdit();
+ boolean endBatchEdit();
/**
* Send a key event to the process that is currently attached
@@ -734,7 +733,7 @@
* @see KeyCharacterMap#PREDICTIVE
* @see KeyCharacterMap#ALPHA
*/
- public boolean sendKeyEvent(KeyEvent event);
+ boolean sendKeyEvent(KeyEvent event);
/**
* Clear the given meta key pressed states in the given input
@@ -749,7 +748,7 @@
* @return true on success, false if the input connection is no longer
* valid.
*/
- public boolean clearMetaKeyStates(int states);
+ boolean clearMetaKeyStates(int states);
/**
* Called back when the connected IME switches between fullscreen and normal modes.
@@ -766,7 +765,7 @@
* devices.
* @see InputMethodManager#isFullscreenMode()
*/
- public boolean reportFullscreenMode(boolean enabled);
+ boolean reportFullscreenMode(boolean enabled);
/**
* API to send private commands from an input method to its
@@ -786,7 +785,7 @@
* associated editor understood it), false if the input connection is no longer
* valid.
*/
- public boolean performPrivateCommand(String action, Bundle data);
+ boolean performPrivateCommand(String action, Bundle data);
/**
* The editor is requested to call
@@ -794,7 +793,7 @@
* once, as soon as possible, regardless of cursor/anchor position changes. This flag can be
* used together with {@link #CURSOR_UPDATE_MONITOR}.
*/
- public static final int CURSOR_UPDATE_IMMEDIATE = 1 << 0;
+ int CURSOR_UPDATE_IMMEDIATE = 1 << 0;
/**
* The editor is requested to call
@@ -805,7 +804,7 @@
* This flag can be used together with {@link #CURSOR_UPDATE_IMMEDIATE}.
* </p>
*/
- public static final int CURSOR_UPDATE_MONITOR = 1 << 1;
+ int CURSOR_UPDATE_MONITOR = 1 << 1;
/**
* Called by the input method to ask the editor for calling back
@@ -821,7 +820,7 @@
* In {@link android.os.Build.VERSION_CODES#N} and later, returns {@code false} also when the
* target application does not implement this method.
*/
- public boolean requestCursorUpdates(int cursorUpdateMode);
+ boolean requestCursorUpdates(int cursorUpdateMode);
/**
* Called by the {@link InputMethodManager} to enable application developers to specify a
@@ -832,7 +831,7 @@
*
* @return {@code null} to use the default {@link Handler}.
*/
- public Handler getHandler();
+ Handler getHandler();
/**
* Called by the system up to only once to notify that the system is about to invalidate
@@ -846,7 +845,7 @@
*
* <p>Note: This does nothing when called from input methods.</p>
*/
- public void closeConnection();
+ void closeConnection();
/**
* When this flag is used, the editor will be able to request read access to the content URI
@@ -863,7 +862,7 @@
* client is able to request a temporary read-only access even after the current IME is switched
* to any other IME as long as the client keeps {@link InputContentInfo} object.</p>
**/
- public static int INPUT_CONTENT_GRANT_READ_URI_PERMISSION =
+ int INPUT_CONTENT_GRANT_READ_URI_PERMISSION =
android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION; // 0x00000001
/**
@@ -897,6 +896,6 @@
* @return {@code true} if this request is accepted by the application, whether the request
* is already handled or still being handled in background, {@code false} otherwise.
*/
- public boolean commitContent(@NonNull InputContentInfo inputContentInfo, int flags,
+ boolean commitContent(@NonNull InputContentInfo inputContentInfo, int flags,
@Nullable Bundle opts);
}
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index 317730c..f671e22 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -1,17 +1,17 @@
/*
- * Copyright (C) 2007-2008 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
- *
+ * Copyright (C) 2007 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.
+ * 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.inputmethod;
@@ -74,6 +74,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public CharSequence getTextBeforeCursor(int n, int flags) {
return mTarget.getTextBeforeCursor(n, flags);
}
@@ -82,6 +83,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public CharSequence getTextAfterCursor(int n, int flags) {
return mTarget.getTextAfterCursor(n, flags);
}
@@ -90,6 +92,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public CharSequence getSelectedText(int flags) {
return mTarget.getSelectedText(flags);
}
@@ -98,6 +101,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public int getCursorCapsMode(int reqModes) {
return mTarget.getCursorCapsMode(reqModes);
}
@@ -106,6 +110,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
return mTarget.getExtractedText(request, flags);
}
@@ -114,6 +119,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
return mTarget.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
}
@@ -122,6 +128,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
return mTarget.deleteSurroundingText(beforeLength, afterLength);
}
@@ -130,6 +137,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean setComposingText(CharSequence text, int newCursorPosition) {
return mTarget.setComposingText(text, newCursorPosition);
}
@@ -138,6 +146,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean setComposingRegion(int start, int end) {
return mTarget.setComposingRegion(start, end);
}
@@ -146,6 +155,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean finishComposingText() {
return mTarget.finishComposingText();
}
@@ -154,6 +164,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean commitText(CharSequence text, int newCursorPosition) {
return mTarget.commitText(text, newCursorPosition);
}
@@ -162,6 +173,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean commitCompletion(CompletionInfo text) {
return mTarget.commitCompletion(text);
}
@@ -170,6 +182,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean commitCorrection(CorrectionInfo correctionInfo) {
return mTarget.commitCorrection(correctionInfo);
}
@@ -178,6 +191,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean setSelection(int start, int end) {
return mTarget.setSelection(start, end);
}
@@ -186,6 +200,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean performEditorAction(int editorAction) {
return mTarget.performEditorAction(editorAction);
}
@@ -194,6 +209,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean performContextMenuAction(int id) {
return mTarget.performContextMenuAction(id);
}
@@ -202,6 +218,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean beginBatchEdit() {
return mTarget.beginBatchEdit();
}
@@ -210,6 +227,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean endBatchEdit() {
return mTarget.endBatchEdit();
}
@@ -218,6 +236,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean sendKeyEvent(KeyEvent event) {
return mTarget.sendKeyEvent(event);
}
@@ -226,6 +245,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean clearMetaKeyStates(int states) {
return mTarget.clearMetaKeyStates(states);
}
@@ -234,6 +254,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean reportFullscreenMode(boolean enabled) {
return mTarget.reportFullscreenMode(enabled);
}
@@ -242,6 +263,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean performPrivateCommand(String action, Bundle data) {
return mTarget.performPrivateCommand(action, data);
}
@@ -250,6 +272,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean requestCursorUpdates(int cursorUpdateMode) {
return mTarget.requestCursorUpdates(cursorUpdateMode);
}
@@ -258,6 +281,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public Handler getHandler() {
return mTarget.getHandler();
}
@@ -266,6 +290,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public void closeConnection() {
mTarget.closeConnection();
}
@@ -274,6 +299,7 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Override
public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
return mTarget.commitContent(inputContentInfo, flags, opts);
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 80d7b6b7..7db5c32 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -337,20 +337,23 @@
int mCursorCandEnd;
/**
- * Represents an invalid action notification sequence number. {@link InputMethodManagerService}
- * always issues a positive integer for action notification sequence numbers. Thus -1 is
- * guaranteed to be different from any valid sequence number.
+ * Represents an invalid action notification sequence number.
+ * {@link com.android.server.InputMethodManagerService} always issues a positive integer for
+ * action notification sequence numbers. Thus {@code -1} is guaranteed to be different from any
+ * valid sequence number.
*/
private static final int NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER = -1;
/**
- * The next sequence number that is to be sent to {@link InputMethodManagerService} via
+ * The next sequence number that is to be sent to
+ * {@link com.android.server.InputMethodManagerService} via
* {@link IInputMethodManager#notifyUserAction(int)} at once when a user action is observed.
*/
private int mNextUserActionNotificationSequenceNumber =
NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER;
/**
- * The last sequence number that is already sent to {@link InputMethodManagerService}.
+ * The last sequence number that is already sent to
+ * {@link com.android.server.InputMethodManagerService}.
*/
private int mLastSentUserActionNotificationSequenceNumber =
NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER;
@@ -1079,15 +1082,15 @@
}
/**
- * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
- * input window should only be hidden if it was not explicitly shown
+ * Flag for {@link #hideSoftInputFromWindow} and {@link InputMethodService#requestHideSelf(int)}
+ * to indicate that the soft input window should only be hidden if it was not explicitly shown
* by the user.
*/
public static final int HIDE_IMPLICIT_ONLY = 0x0001;
/**
- * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
- * input window should normally be hidden, unless it was originally
+ * Flag for {@link #hideSoftInputFromWindow} and {@link InputMethodService#requestShowSelf(int)}
+ * to indicate that the soft input window should normally be hidden, unless it was originally
* shown with {@link #SHOW_FORCED}.
*/
public static final int HIDE_NOT_ALWAYS = 0x0002;
@@ -1255,12 +1258,7 @@
// The view is running on a different thread than our own, so
// we need to reschedule our work for over there.
if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread");
- vh.post(new Runnable() {
- @Override
- public void run() {
- startInputInner(startInputReason, null, 0, 0, 0);
- }
- });
+ vh.post(() -> startInputInner(startInputReason, null, 0, 0, 0));
return false;
}
@@ -1871,9 +1869,9 @@
* @param flags Provides additional operating flags. Currently may be
* 0 or have the {@link #HIDE_IMPLICIT_ONLY},
* {@link #HIDE_NOT_ALWAYS} bit set.
- * @deprecated Use {@link InputMethodService#hideSoftInputFromInputMethod(int)}
- * instead. This method was intended for IME developers who should be accessing APIs through
- * the service. APIs in this class are intended for app developers interacting with the IME.
+ * @deprecated Use {@link InputMethodService#requestHideSelf(int)} instead. This method was
+ * intended for IME developers who should be accessing APIs through the service. APIs in this
+ * class are intended for app developers interacting with the IME.
*/
@Deprecated
public void hideSoftInputFromInputMethod(IBinder token, int flags) {
@@ -1903,9 +1901,9 @@
* @param flags Provides additional operating flags. Currently may be
* 0 or have the {@link #SHOW_IMPLICIT} or
* {@link #SHOW_FORCED} bit set.
- * @deprecated Use {@link InputMethodService#showSoftInputFromInputMethod(int)}
- * instead. This method was intended for IME developers who should be accessing APIs through
- * the service. APIs in this class are intended for app developers interacting with the IME.
+ * @deprecated Use {@link InputMethodService#requestShowSelf(int)} instead. This method was
+ * intended for IME developers who should be accessing APIs through the service. APIs in this
+ * class are intended for app developers interacting with the IME.
*/
@Deprecated
public void showSoftInputFromInputMethod(IBinder token, int flags) {
@@ -2429,8 +2427,8 @@
* Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
* permission to the content.
*
- * <p>See {@link android.inputmethodservice.InputMethodService#exposeContent(InputContentInfo, EditorInfo)}
- * for details.</p>
+ * <p>See {@link android.inputmethodservice.InputMethodService#exposeContent(InputContentInfo,
+ * InputConnection)} for details.</p>
*
* @param token Supplies the identifying token given to an input method when it was started,
* which allows it to perform this operation on itself.
diff --git a/core/java/android/view/textclassifier/EntityConfidence.java b/core/java/android/view/textclassifier/EntityConfidence.java
index 19660d9..69a59a5 100644
--- a/core/java/android/view/textclassifier/EntityConfidence.java
+++ b/core/java/android/view/textclassifier/EntityConfidence.java
@@ -18,6 +18,8 @@
import android.annotation.FloatRange;
import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.ArrayMap;
import com.android.internal.util.Preconditions;
@@ -30,17 +32,16 @@
/**
* Helper object for setting and getting entity scores for classified text.
*
- * @param <T> the entity type.
* @hide
*/
-final class EntityConfidence<T> {
+final class EntityConfidence implements Parcelable {
- private final ArrayMap<T, Float> mEntityConfidence = new ArrayMap<>();
- private final ArrayList<T> mSortedEntities = new ArrayList<>();
+ private final ArrayMap<String, Float> mEntityConfidence = new ArrayMap<>();
+ private final ArrayList<String> mSortedEntities = new ArrayList<>();
EntityConfidence() {}
- EntityConfidence(@NonNull EntityConfidence<T> source) {
+ EntityConfidence(@NonNull EntityConfidence source) {
Preconditions.checkNotNull(source);
mEntityConfidence.putAll(source.mEntityConfidence);
mSortedEntities.addAll(source.mSortedEntities);
@@ -54,24 +55,16 @@
* @param source a map from entity to a confidence value in the range 0 (low confidence) to
* 1 (high confidence).
*/
- EntityConfidence(@NonNull Map<T, Float> source) {
+ EntityConfidence(@NonNull Map<String, Float> source) {
Preconditions.checkNotNull(source);
// Prune non-existent entities and clamp to 1.
mEntityConfidence.ensureCapacity(source.size());
- for (Map.Entry<T, Float> it : source.entrySet()) {
+ for (Map.Entry<String, Float> it : source.entrySet()) {
if (it.getValue() <= 0) continue;
mEntityConfidence.put(it.getKey(), Math.min(1, it.getValue()));
}
-
- // Create a list of entities sorted by decreasing confidence for getEntities().
- mSortedEntities.ensureCapacity(mEntityConfidence.size());
- mSortedEntities.addAll(mEntityConfidence.keySet());
- mSortedEntities.sort((e1, e2) -> {
- float score1 = mEntityConfidence.get(e1);
- float score2 = mEntityConfidence.get(e2);
- return Float.compare(score2, score1);
- });
+ resetSortedEntitiesFromMap();
}
/**
@@ -79,7 +72,7 @@
* high confidence to low confidence.
*/
@NonNull
- public List<T> getEntities() {
+ public List<String> getEntities() {
return Collections.unmodifiableList(mSortedEntities);
}
@@ -89,7 +82,7 @@
* classified text.
*/
@FloatRange(from = 0.0, to = 1.0)
- public float getConfidenceScore(T entity) {
+ public float getConfidenceScore(String entity) {
if (mEntityConfidence.containsKey(entity)) {
return mEntityConfidence.get(entity);
}
@@ -100,4 +93,51 @@
public String toString() {
return mEntityConfidence.toString();
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mEntityConfidence.size());
+ for (Map.Entry<String, Float> entry : mEntityConfidence.entrySet()) {
+ dest.writeString(entry.getKey());
+ dest.writeFloat(entry.getValue());
+ }
+ }
+
+ public static final Parcelable.Creator<EntityConfidence> CREATOR =
+ new Parcelable.Creator<EntityConfidence>() {
+ @Override
+ public EntityConfidence createFromParcel(Parcel in) {
+ return new EntityConfidence(in);
+ }
+
+ @Override
+ public EntityConfidence[] newArray(int size) {
+ return new EntityConfidence[size];
+ }
+ };
+
+ private EntityConfidence(Parcel in) {
+ final int numEntities = in.readInt();
+ mEntityConfidence.ensureCapacity(numEntities);
+ for (int i = 0; i < numEntities; ++i) {
+ mEntityConfidence.put(in.readString(), in.readFloat());
+ }
+ resetSortedEntitiesFromMap();
+ }
+
+ private void resetSortedEntitiesFromMap() {
+ mSortedEntities.clear();
+ mSortedEntities.ensureCapacity(mEntityConfidence.size());
+ mSortedEntities.addAll(mEntityConfidence.keySet());
+ mSortedEntities.sort((e1, e2) -> {
+ float score1 = mEntityConfidence.get(e1);
+ float score2 = mEntityConfidence.get(e2);
+ return Float.compare(score2, score1);
+ });
+ }
}
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 7ffbf63..7089677 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -22,8 +22,13 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.LocaleList;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.ArrayMap;
import android.view.View.OnClickListener;
import android.view.textclassifier.TextClassifier.EntityType;
@@ -52,7 +57,7 @@
* Button button = new Button(context);
* button.setCompoundDrawablesWithIntrinsicBounds(classification.getIcon(), null, null, null);
* button.setText(classification.getLabel());
- * button.setOnClickListener(classification.getOnClickListener());
+ * button.setOnClickListener(v -> context.startActivity(classification.getIntent()));
* }</pre>
*
* <p>e.g. starting an action mode with menu items that can handle the classified text:
@@ -90,7 +95,6 @@
* ...
* });
* }</pre>
- *
*/
public final class TextClassification {
@@ -99,6 +103,10 @@
*/
static final TextClassification EMPTY = new TextClassification.Builder().build();
+ // TODO(toki): investigate a way to derive this based on device properties.
+ private static final int MAX_PRIMARY_ICON_SIZE = 192;
+ private static final int MAX_SECONDARY_ICON_SIZE = 144;
+
@NonNull private final String mText;
@Nullable private final Drawable mPrimaryIcon;
@Nullable private final String mPrimaryLabel;
@@ -107,8 +115,7 @@
@NonNull private final List<Drawable> mSecondaryIcons;
@NonNull private final List<String> mSecondaryLabels;
@NonNull private final List<Intent> mSecondaryIntents;
- @NonNull private final List<OnClickListener> mSecondaryOnClickListeners;
- @NonNull private final EntityConfidence<String> mEntityConfidence;
+ @NonNull private final EntityConfidence mEntityConfidence;
@NonNull private final String mSignature;
private TextClassification(
@@ -120,12 +127,10 @@
@NonNull List<Drawable> secondaryIcons,
@NonNull List<String> secondaryLabels,
@NonNull List<Intent> secondaryIntents,
- @NonNull List<OnClickListener> secondaryOnClickListeners,
@NonNull Map<String, Float> entityConfidence,
@NonNull String signature) {
Preconditions.checkArgument(secondaryLabels.size() == secondaryIntents.size());
Preconditions.checkArgument(secondaryIcons.size() == secondaryIntents.size());
- Preconditions.checkArgument(secondaryOnClickListeners.size() == secondaryIntents.size());
mText = text;
mPrimaryIcon = primaryIcon;
mPrimaryLabel = primaryLabel;
@@ -134,8 +139,7 @@
mSecondaryIcons = secondaryIcons;
mSecondaryLabels = secondaryLabels;
mSecondaryIntents = secondaryIntents;
- mSecondaryOnClickListeners = secondaryOnClickListeners;
- mEntityConfidence = new EntityConfidence<>(entityConfidence);
+ mEntityConfidence = new EntityConfidence(entityConfidence);
mSignature = signature;
}
@@ -186,7 +190,6 @@
* @see #getSecondaryIntent(int)
* @see #getSecondaryLabel(int)
* @see #getSecondaryIcon(int)
- * @see #getSecondaryOnClickListener(int)
*/
@IntRange(from = 0)
public int getSecondaryActionsCount() {
@@ -198,13 +201,10 @@
* classified text.
*
* @param index Index of the action to get the icon for.
- *
* @throws IndexOutOfBoundsException if the specified index is out of range.
- *
* @see #getSecondaryActionsCount() for the number of actions available.
* @see #getSecondaryIntent(int)
* @see #getSecondaryLabel(int)
- * @see #getSecondaryOnClickListener(int)
* @see #getIcon()
*/
@Nullable
@@ -228,13 +228,10 @@
* the classified text.
*
* @param index Index of the action to get the label for.
- *
* @throws IndexOutOfBoundsException if the specified index is out of range.
- *
* @see #getSecondaryActionsCount()
* @see #getSecondaryIntent(int)
* @see #getSecondaryIcon(int)
- * @see #getSecondaryOnClickListener(int)
* @see #getLabel()
*/
@Nullable
@@ -257,13 +254,10 @@
* Returns one of the <i>secondary</i> intents that may be fired to act on the classified text.
*
* @param index Index of the action to get the intent for.
- *
* @throws IndexOutOfBoundsException if the specified index is out of range.
- *
* @see #getSecondaryActionsCount()
* @see #getSecondaryLabel(int)
* @see #getSecondaryIcon(int)
- * @see #getSecondaryOnClickListener(int)
* @see #getIntent()
*/
@Nullable
@@ -282,29 +276,10 @@
}
/**
- * Returns one of the <i>secondary</i> OnClickListeners that may be triggered to act on the
- * classified text.
- *
- * @param index Index of the action to get the click listener for.
- *
- * @throws IndexOutOfBoundsException if the specified index is out of range.
- *
- * @see #getSecondaryActionsCount()
- * @see #getSecondaryIntent(int)
- * @see #getSecondaryLabel(int)
- * @see #getSecondaryIcon(int)
- * @see #getOnClickListener()
- */
- @Nullable
- public OnClickListener getSecondaryOnClickListener(int index) {
- return mSecondaryOnClickListeners.get(index);
- }
-
- /**
* Returns the <i>primary</i> OnClickListener that may be triggered to act on the classified
- * text.
- *
- * @see #getSecondaryOnClickListener(int)
+ * text. This field is not parcelable and will be null for all objects read from a parcel.
+ * Instead, call Context#startActivity(Intent) with the result of #getSecondaryIntent(int).
+ * Note that this may fail if the activity doesn't have permission to send the intent.
*/
@Nullable
public OnClickListener getOnClickListener() {
@@ -334,6 +309,42 @@
mSignature);
}
+ /** Helper for parceling via #ParcelableWrapper. */
+ private void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mText);
+ final Bitmap primaryIconBitmap = drawableToBitmap(mPrimaryIcon, MAX_PRIMARY_ICON_SIZE);
+ dest.writeInt(primaryIconBitmap != null ? 1 : 0);
+ if (primaryIconBitmap != null) {
+ primaryIconBitmap.writeToParcel(dest, flags);
+ }
+ dest.writeString(mPrimaryLabel);
+ dest.writeInt(mPrimaryIntent != null ? 1 : 0);
+ if (mPrimaryIntent != null) {
+ mPrimaryIntent.writeToParcel(dest, flags);
+ }
+ // mPrimaryOnClickListener is not parcelable.
+ dest.writeTypedList(drawablesToBitmaps(mSecondaryIcons, MAX_SECONDARY_ICON_SIZE));
+ dest.writeStringList(mSecondaryLabels);
+ dest.writeTypedList(mSecondaryIntents);
+ mEntityConfidence.writeToParcel(dest, flags);
+ dest.writeString(mSignature);
+ }
+
+ /** Helper for unparceling via #ParcelableWrapper. */
+ private TextClassification(Parcel in) {
+ mText = in.readString();
+ mPrimaryIcon = in.readInt() == 0
+ ? null : new BitmapDrawable(null, Bitmap.CREATOR.createFromParcel(in));
+ mPrimaryLabel = in.readString();
+ mPrimaryIntent = in.readInt() == 0 ? null : Intent.CREATOR.createFromParcel(in);
+ mPrimaryOnClickListener = null; // not parcelable
+ mSecondaryIcons = bitmapsToDrawables(in.createTypedArrayList(Bitmap.CREATOR));
+ mSecondaryLabels = in.createStringArrayList();
+ mSecondaryIntents = in.createTypedArrayList(Intent.CREATOR);
+ mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
+ mSignature = in.readString();
+ }
+
/**
* Creates an OnClickListener that starts an activity with the specified intent.
*
@@ -349,6 +360,68 @@
}
/**
+ * Returns a Bitmap representation of the Drawable
+ *
+ * @param drawable The drawable to convert.
+ * @param maxDims The maximum edge length of the resulting bitmap (in pixels).
+ */
+ @Nullable
+ private static Bitmap drawableToBitmap(@Nullable Drawable drawable, int maxDims) {
+ if (drawable == null) {
+ return null;
+ }
+ final int actualWidth = Math.max(1, drawable.getIntrinsicWidth());
+ final int actualHeight = Math.max(1, drawable.getIntrinsicHeight());
+ final double scaleWidth = ((double) maxDims) / actualWidth;
+ final double scaleHeight = ((double) maxDims) / actualHeight;
+ final double scale = Math.min(1.0, Math.min(scaleWidth, scaleHeight));
+ final int width = (int) (actualWidth * scale);
+ final int height = (int) (actualHeight * scale);
+ if (drawable instanceof BitmapDrawable) {
+ final BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
+ if (actualWidth != width || actualHeight != height) {
+ return Bitmap.createScaledBitmap(
+ bitmapDrawable.getBitmap(), width, height, /*filter=*/false);
+ } else {
+ return bitmapDrawable.getBitmap();
+ }
+ } else {
+ final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
+ return bitmap;
+ }
+ }
+
+ /**
+ * Returns a list of drawables converted to Bitmaps
+ *
+ * @param drawables The drawables to convert.
+ * @param maxDims The maximum edge length of the resulting bitmaps (in pixels).
+ */
+ private static List<Bitmap> drawablesToBitmaps(List<Drawable> drawables, int maxDims) {
+ final List<Bitmap> bitmaps = new ArrayList<>(drawables.size());
+ for (Drawable drawable : drawables) {
+ bitmaps.add(drawableToBitmap(drawable, maxDims));
+ }
+ return bitmaps;
+ }
+
+ /** Returns a list of drawable wrappers for a list of bitmaps. */
+ private static List<Drawable> bitmapsToDrawables(List<Bitmap> bitmaps) {
+ final List<Drawable> drawables = new ArrayList<>(bitmaps.size());
+ for (Bitmap bitmap : bitmaps) {
+ if (bitmap != null) {
+ drawables.add(new BitmapDrawable(null, bitmap));
+ } else {
+ drawables.add(null);
+ }
+ }
+ return drawables;
+ }
+
+ /**
* Builder for building {@link TextClassification} objects.
*
* <p>e.g.
@@ -358,9 +431,9 @@
* .setText(classifiedText)
* .setEntityType(TextClassifier.TYPE_EMAIL, 0.9)
* .setEntityType(TextClassifier.TYPE_OTHER, 0.1)
- * .setPrimaryAction(intent, label, icon, onClickListener)
- * .addSecondaryAction(intent1, label1, icon1, onClickListener1)
- * .addSecondaryAction(intent2, label2, icon2, onClickListener2)
+ * .setPrimaryAction(intent, label, icon)
+ * .addSecondaryAction(intent1, label1, icon1)
+ * .addSecondaryAction(intent2, label2, icon2)
* .build();
* }</pre>
*/
@@ -370,7 +443,6 @@
@NonNull private final List<Drawable> mSecondaryIcons = new ArrayList<>();
@NonNull private final List<String> mSecondaryLabels = new ArrayList<>();
@NonNull private final List<Intent> mSecondaryIntents = new ArrayList<>();
- @NonNull private final List<OnClickListener> mSecondaryOnClickListeners = new ArrayList<>();
@NonNull private final Map<String, Float> mEntityConfidence = new ArrayMap<>();
@Nullable Drawable mPrimaryIcon;
@Nullable String mPrimaryLabel;
@@ -413,16 +485,14 @@
* <p><stong>Note: </stong> If all input parameters are set to null, this method will be a
* no-op.
*
- * @see #setPrimaryAction(Intent, String, Drawable, OnClickListener)
+ * @see #setPrimaryAction(Intent, String, Drawable)
*/
public Builder addSecondaryAction(
- @Nullable Intent intent, @Nullable String label, @Nullable Drawable icon,
- @Nullable OnClickListener onClickListener) {
- if (intent != null || label != null || icon != null || onClickListener != null) {
+ @Nullable Intent intent, @Nullable String label, @Nullable Drawable icon) {
+ if (intent != null || label != null || icon != null) {
mSecondaryIntents.add(intent);
mSecondaryLabels.add(label);
mSecondaryIcons.add(icon);
- mSecondaryOnClickListeners.add(onClickListener);
}
return this;
}
@@ -432,7 +502,6 @@
*/
public Builder clearSecondaryActions() {
mSecondaryIntents.clear();
- mSecondaryOnClickListeners.clear();
mSecondaryLabels.clear();
mSecondaryIcons.clear();
return this;
@@ -440,26 +509,23 @@
/**
* Sets the <i>primary</i> action that may be performed on the classified text. This is
- * equivalent to calling {@code
- * setIntent(intent).setLabel(label).setIcon(icon).setOnClickListener(onClickListener)}.
+ * equivalent to calling {@code setIntent(intent).setLabel(label).setIcon(icon)}.
*
* <p><strong>Note: </strong>If all input parameters are null, there will be no
* <i>primary</i> action but there may still be <i>secondary</i> actions.
*
- * @see #addSecondaryAction(Intent, String, Drawable, OnClickListener)
+ * @see #addSecondaryAction(Intent, String, Drawable)
*/
public Builder setPrimaryAction(
- @Nullable Intent intent, @Nullable String label, @Nullable Drawable icon,
- @Nullable OnClickListener onClickListener) {
- return setIntent(intent).setLabel(label).setIcon(icon)
- .setOnClickListener(onClickListener);
+ @Nullable Intent intent, @Nullable String label, @Nullable Drawable icon) {
+ return setIntent(intent).setLabel(label).setIcon(icon);
}
/**
* Sets the icon for the <i>primary</i> action that may be rendered on a widget used to act
* on the classified text.
*
- * @see #setPrimaryAction(Intent, String, Drawable, OnClickListener)
+ * @see #setPrimaryAction(Intent, String, Drawable)
*/
public Builder setIcon(@Nullable Drawable icon) {
mPrimaryIcon = icon;
@@ -470,7 +536,7 @@
* Sets the label for the <i>primary</i> action that may be rendered on a widget used to
* act on the classified text.
*
- * @see #setPrimaryAction(Intent, String, Drawable, OnClickListener)
+ * @see #setPrimaryAction(Intent, String, Drawable)
*/
public Builder setLabel(@Nullable String label) {
mPrimaryLabel = label;
@@ -481,7 +547,7 @@
* Sets the intent for the <i>primary</i> action that may be fired to act on the classified
* text.
*
- * @see #setPrimaryAction(Intent, String, Drawable, OnClickListener)
+ * @see #setPrimaryAction(Intent, String, Drawable)
*/
public Builder setIntent(@Nullable Intent intent) {
mPrimaryIntent = intent;
@@ -490,9 +556,8 @@
/**
* Sets the OnClickListener for the <i>primary</i> action that may be triggered to act on
- * the classified text.
- *
- * @see #setPrimaryAction(Intent, String, Drawable, OnClickListener)
+ * the classified text. This field is not parcelable and will always be null when the
+ * object is read from a parcel.
*/
public Builder setOnClickListener(@Nullable OnClickListener onClickListener) {
mPrimaryOnClickListener = onClickListener;
@@ -515,10 +580,8 @@
public TextClassification build() {
return new TextClassification(
mText,
- mPrimaryIcon, mPrimaryLabel,
- mPrimaryIntent, mPrimaryOnClickListener,
- mSecondaryIcons, mSecondaryLabels,
- mSecondaryIntents, mSecondaryOnClickListeners,
+ mPrimaryIcon, mPrimaryLabel, mPrimaryIntent, mPrimaryOnClickListener,
+ mSecondaryIcons, mSecondaryLabels, mSecondaryIntents,
mEntityConfidence, mSignature);
}
}
@@ -526,9 +589,11 @@
/**
* Optional input parameters for generating TextClassification.
*/
- public static final class Options {
+ public static final class Options implements Parcelable {
- private LocaleList mDefaultLocales;
+ private @Nullable LocaleList mDefaultLocales;
+
+ public Options() {}
/**
* @param defaultLocales ordered list of locale preferences that may be used to disambiguate
@@ -548,5 +613,80 @@
public LocaleList getDefaultLocales() {
return mDefaultLocales;
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mDefaultLocales != null ? 1 : 0);
+ if (mDefaultLocales != null) {
+ mDefaultLocales.writeToParcel(dest, flags);
+ }
+ }
+
+ public static final Parcelable.Creator<Options> CREATOR =
+ new Parcelable.Creator<Options>() {
+ @Override
+ public Options createFromParcel(Parcel in) {
+ return new Options(in);
+ }
+
+ @Override
+ public Options[] newArray(int size) {
+ return new Options[size];
+ }
+ };
+
+ private Options(Parcel in) {
+ if (in.readInt() > 0) {
+ mDefaultLocales = LocaleList.CREATOR.createFromParcel(in);
+ }
+ }
+ }
+
+ /**
+ * Parcelable wrapper for TextClassification objects.
+ * @hide
+ */
+ public static final class ParcelableWrapper implements Parcelable {
+
+ @NonNull private TextClassification mTextClassification;
+
+ public ParcelableWrapper(@NonNull TextClassification textClassification) {
+ Preconditions.checkNotNull(textClassification);
+ mTextClassification = textClassification;
+ }
+
+ @NonNull
+ public TextClassification getTextClassification() {
+ return mTextClassification;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ mTextClassification.writeToParcel(dest, flags);
+ }
+
+ public static final Parcelable.Creator<ParcelableWrapper> CREATOR =
+ new Parcelable.Creator<ParcelableWrapper>() {
+ @Override
+ public ParcelableWrapper createFromParcel(Parcel in) {
+ return new ParcelableWrapper(new TextClassification(in));
+ }
+
+ @Override
+ public ParcelableWrapper[] newArray(int size) {
+ return new ParcelableWrapper[size];
+ }
+ };
+
}
}
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index ed60430..e9715c5 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -23,6 +23,8 @@
import android.annotation.StringDef;
import android.annotation.WorkerThread;
import android.os.LocaleList;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.ArraySet;
import com.android.internal.util.Preconditions;
@@ -275,8 +277,8 @@
/**
* Returns a {@link Collection} of the entity types in the specified preset.
*
- * @see #ENTITIES_ALL
- * @see #ENTITIES_NONE
+ * @see #ENTITY_PRESET_ALL
+ * @see #ENTITY_PRESET_NONE
*/
default Collection<String> getEntitiesForPreset(@EntityPreset int entityPreset) {
return Collections.EMPTY_LIST;
@@ -305,7 +307,7 @@
*
* Configs are initially based on a predefined preset, and can be modified from there.
*/
- final class EntityConfig {
+ final class EntityConfig implements Parcelable {
private final @TextClassifier.EntityPreset int mEntityPreset;
private final Collection<String> mExcludedEntityTypes;
private final Collection<String> mIncludedEntityTypes;
@@ -355,6 +357,37 @@
}
return Collections.unmodifiableList(entities);
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mEntityPreset);
+ dest.writeStringList(new ArrayList<>(mExcludedEntityTypes));
+ dest.writeStringList(new ArrayList<>(mIncludedEntityTypes));
+ }
+
+ public static final Parcelable.Creator<EntityConfig> CREATOR =
+ new Parcelable.Creator<EntityConfig>() {
+ @Override
+ public EntityConfig createFromParcel(Parcel in) {
+ return new EntityConfig(in);
+ }
+
+ @Override
+ public EntityConfig[] newArray(int size) {
+ return new EntityConfig[size];
+ }
+ };
+
+ private EntityConfig(Parcel in) {
+ mEntityPreset = in.readInt();
+ mExcludedEntityTypes = new ArraySet<>(in.createStringArrayList());
+ mIncludedEntityTypes = new ArraySet<>(in.createStringArrayList());
+ }
}
/**
diff --git a/core/java/android/view/textclassifier/TextClassifierConstants.java b/core/java/android/view/textclassifier/TextClassifierConstants.java
index 51e6168..00695b7 100644
--- a/core/java/android/view/textclassifier/TextClassifierConstants.java
+++ b/core/java/android/view/textclassifier/TextClassifierConstants.java
@@ -45,19 +45,24 @@
"smart_selection_dark_launch";
private static final String SMART_SELECTION_ENABLED_FOR_EDIT_TEXT =
"smart_selection_enabled_for_edit_text";
+ private static final String SMART_LINKIFY_ENABLED =
+ "smart_linkify_enabled";
private static final boolean SMART_SELECTION_DARK_LAUNCH_DEFAULT = false;
private static final boolean SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT = true;
+ private static final boolean SMART_LINKIFY_ENABLED_DEFAULT = true;
/** Default settings. */
static final TextClassifierConstants DEFAULT = new TextClassifierConstants();
private final boolean mDarkLaunch;
private final boolean mSuggestSelectionEnabledForEditableText;
+ private final boolean mSmartLinkifyEnabled;
private TextClassifierConstants() {
mDarkLaunch = SMART_SELECTION_DARK_LAUNCH_DEFAULT;
mSuggestSelectionEnabledForEditableText = SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT;
+ mSmartLinkifyEnabled = SMART_LINKIFY_ENABLED_DEFAULT;
}
private TextClassifierConstants(@Nullable String settings) {
@@ -74,6 +79,9 @@
mSuggestSelectionEnabledForEditableText = parser.getBoolean(
SMART_SELECTION_ENABLED_FOR_EDIT_TEXT,
SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT);
+ mSmartLinkifyEnabled = parser.getBoolean(
+ SMART_LINKIFY_ENABLED,
+ SMART_LINKIFY_ENABLED_DEFAULT);
}
static TextClassifierConstants loadFromString(String settings) {
@@ -87,4 +95,8 @@
public boolean isSuggestSelectionEnabledForEditableText() {
return mSuggestSelectionEnabledForEditableText;
}
+
+ public boolean isSmartLinkifyEnabled() {
+ return mSmartLinkifyEnabled;
+ }
}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index aea3cb0..7db0e76 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -32,7 +32,6 @@
import android.provider.Settings;
import android.text.util.Linkify;
import android.util.Patterns;
-import android.view.View.OnClickListener;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
@@ -187,6 +186,11 @@
Utils.validateInput(text);
final String textString = text.toString();
final TextLinks.Builder builder = new TextLinks.Builder(textString);
+
+ if (!getSettings().isSmartLinkifyEnabled()) {
+ return builder.build();
+ }
+
try {
final LocaleList defaultLocales = options != null ? options.getDefaultLocales() : null;
final Collection<String> entitiesToIdentify =
@@ -457,12 +461,10 @@
}
}
final String labelString = (label != null) ? label.toString() : null;
- final OnClickListener onClickListener =
- TextClassification.createStartActivityOnClickListener(mContext, intent);
if (i == 0) {
- builder.setPrimaryAction(intent, labelString, icon, onClickListener);
+ builder.setPrimaryAction(intent, labelString, icon);
} else {
- builder.addSecondaryAction(intent, labelString, icon, onClickListener);
+ builder.addSecondaryAction(intent, labelString, icon);
}
}
}
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 6c587cf..ba854e0 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -20,6 +20,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.LocaleList;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.text.SpannableString;
import android.text.style.ClickableSpan;
import android.view.View;
@@ -38,7 +40,7 @@
* A collection of links, representing subsequences of text and the entity types (phone number,
* address, url, etc) they may be.
*/
-public final class TextLinks {
+public final class TextLinks implements Parcelable {
private final String mFullText;
private final List<TextLink> mLinks;
@@ -83,11 +85,40 @@
return true;
}
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mFullText);
+ dest.writeTypedList(mLinks);
+ }
+
+ public static final Parcelable.Creator<TextLinks> CREATOR =
+ new Parcelable.Creator<TextLinks>() {
+ @Override
+ public TextLinks createFromParcel(Parcel in) {
+ return new TextLinks(in);
+ }
+
+ @Override
+ public TextLinks[] newArray(int size) {
+ return new TextLinks[size];
+ }
+ };
+
+ private TextLinks(Parcel in) {
+ mFullText = in.readString();
+ mLinks = in.createTypedArrayList(TextLink.CREATOR);
+ }
+
/**
* A link, identifying a substring of text and possible entity types for it.
*/
- public static final class TextLink {
- private final EntityConfidence<String> mEntityScores;
+ public static final class TextLink implements Parcelable {
+ private final EntityConfidence mEntityScores;
private final String mOriginalText;
private final int mStart;
private final int mEnd;
@@ -105,7 +136,7 @@
mOriginalText = originalText;
mStart = start;
mEnd = end;
- mEntityScores = new EntityConfidence<>(entityScores);
+ mEntityScores = new EntityConfidence(entityScores);
}
/**
@@ -153,16 +184,51 @@
@TextClassifier.EntityType String entityType) {
return mEntityScores.getConfidenceScore(entityType);
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ mEntityScores.writeToParcel(dest, flags);
+ dest.writeString(mOriginalText);
+ dest.writeInt(mStart);
+ dest.writeInt(mEnd);
+ }
+
+ public static final Parcelable.Creator<TextLink> CREATOR =
+ new Parcelable.Creator<TextLink>() {
+ @Override
+ public TextLink createFromParcel(Parcel in) {
+ return new TextLink(in);
+ }
+
+ @Override
+ public TextLink[] newArray(int size) {
+ return new TextLink[size];
+ }
+ };
+
+ private TextLink(Parcel in) {
+ mEntityScores = EntityConfidence.CREATOR.createFromParcel(in);
+ mOriginalText = in.readString();
+ mStart = in.readInt();
+ mEnd = in.readInt();
+ }
}
/**
* Optional input parameters for generating TextLinks.
*/
- public static final class Options {
+ public static final class Options implements Parcelable {
private LocaleList mDefaultLocales;
private TextClassifier.EntityConfig mEntityConfig;
+ public Options() {}
+
/**
* @param defaultLocales ordered list of locale preferences that may be used to
* disambiguate the provided text. If no locale preferences exist,
@@ -201,6 +267,45 @@
public TextClassifier.EntityConfig getEntityConfig() {
return mEntityConfig;
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mDefaultLocales != null ? 1 : 0);
+ if (mDefaultLocales != null) {
+ mDefaultLocales.writeToParcel(dest, flags);
+ }
+ dest.writeInt(mEntityConfig != null ? 1 : 0);
+ if (mEntityConfig != null) {
+ mEntityConfig.writeToParcel(dest, flags);
+ }
+ }
+
+ public static final Parcelable.Creator<Options> CREATOR =
+ new Parcelable.Creator<Options>() {
+ @Override
+ public Options createFromParcel(Parcel in) {
+ return new Options(in);
+ }
+
+ @Override
+ public Options[] newArray(int size) {
+ return new Options[size];
+ }
+ };
+
+ private Options(Parcel in) {
+ if (in.readInt() > 0) {
+ mDefaultLocales = LocaleList.CREATOR.createFromParcel(in);
+ }
+ if (in.readInt() > 0) {
+ mEntityConfig = TextClassifier.EntityConfig.CREATOR.createFromParcel(in);
+ }
+ }
}
/**
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index 25e9e7e..774d42d 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -21,6 +21,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.LocaleList;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.ArrayMap;
import android.view.textclassifier.TextClassifier.EntityType;
@@ -36,7 +38,7 @@
private final int mStartIndex;
private final int mEndIndex;
- @NonNull private final EntityConfidence<String> mEntityConfidence;
+ @NonNull private final EntityConfidence mEntityConfidence;
@NonNull private final String mSignature;
private TextSelection(
@@ -44,7 +46,7 @@
@NonNull String signature) {
mStartIndex = startIndex;
mEndIndex = endIndex;
- mEntityConfidence = new EntityConfidence<>(entityConfidence);
+ mEntityConfidence = new EntityConfidence(entityConfidence);
mSignature = signature;
}
@@ -110,6 +112,22 @@
mStartIndex, mEndIndex, mEntityConfidence, mSignature);
}
+ /** Helper for parceling via #ParcelableWrapper. */
+ private void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mStartIndex);
+ dest.writeInt(mEndIndex);
+ mEntityConfidence.writeToParcel(dest, flags);
+ dest.writeString(mSignature);
+ }
+
+ /** Helper for unparceling via #ParcelableWrapper. */
+ private TextSelection(Parcel in) {
+ mStartIndex = in.readInt();
+ mEndIndex = in.readInt();
+ mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
+ mSignature = in.readString();
+ }
+
/**
* Builder used to build {@link TextSelection} objects.
*/
@@ -170,11 +188,13 @@
/**
* Optional input parameters for generating TextSelection.
*/
- public static final class Options {
+ public static final class Options implements Parcelable {
- private LocaleList mDefaultLocales;
+ private @Nullable LocaleList mDefaultLocales;
private boolean mDarkLaunchAllowed;
+ public Options() {}
+
/**
* @param defaultLocales ordered list of locale preferences that may be used to disambiguate
* the provided text. If no locale preferences exist, set this to null or an empty
@@ -216,5 +236,82 @@
public boolean isDarkLaunchAllowed() {
return mDarkLaunchAllowed;
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mDefaultLocales != null ? 1 : 0);
+ if (mDefaultLocales != null) {
+ mDefaultLocales.writeToParcel(dest, flags);
+ }
+ dest.writeInt(mDarkLaunchAllowed ? 1 : 0);
+ }
+
+ public static final Parcelable.Creator<Options> CREATOR =
+ new Parcelable.Creator<Options>() {
+ @Override
+ public Options createFromParcel(Parcel in) {
+ return new Options(in);
+ }
+
+ @Override
+ public Options[] newArray(int size) {
+ return new Options[size];
+ }
+ };
+
+ private Options(Parcel in) {
+ if (in.readInt() > 0) {
+ mDefaultLocales = LocaleList.CREATOR.createFromParcel(in);
+ }
+ mDarkLaunchAllowed = in.readInt() != 0;
+ }
+ }
+
+ /**
+ * Parcelable wrapper for TextSelection objects.
+ * @hide
+ */
+ public static final class ParcelableWrapper implements Parcelable {
+
+ @NonNull private TextSelection mTextSelection;
+
+ public ParcelableWrapper(@NonNull TextSelection textSelection) {
+ Preconditions.checkNotNull(textSelection);
+ mTextSelection = textSelection;
+ }
+
+ @NonNull
+ public TextSelection getTextSelection() {
+ return mTextSelection;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ mTextSelection.writeToParcel(dest, flags);
+ }
+
+ public static final Parcelable.Creator<ParcelableWrapper> CREATOR =
+ new Parcelable.Creator<ParcelableWrapper>() {
+ @Override
+ public ParcelableWrapper createFromParcel(Parcel in) {
+ return new ParcelableWrapper(new TextSelection(in));
+ }
+
+ @Override
+ public ParcelableWrapper[] newArray(int size) {
+ return new ParcelableWrapper[size];
+ }
+ };
+
}
}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index b3522ec..e9fe481 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -27,7 +27,6 @@
import android.content.pm.Signature;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.StrictMode;
import android.os.Trace;
import android.util.AndroidRuntimeException;
import android.util.ArraySet;
@@ -251,7 +250,6 @@
"WebView.disableWebView() was called: WebView is disabled");
}
- StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()");
try {
Class<WebViewFactoryProvider> providerClass = getProviderClass();
@@ -279,7 +277,6 @@
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
- StrictMode.setThreadPolicy(oldPolicy);
}
}
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index e0c897d..6bee58f 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -6849,7 +6849,7 @@
// detached and we do not allow detached views to fire accessibility
// events. So we are announcing that the subtree changed giving a chance
// to clients holding on to a view in this subtree to refresh it.
- notifyViewAccessibilityStateChangedIfNeeded(
+ notifyAccessibilityStateChanged(
AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
// Don't scrap views that have transient state.
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 6c19256..08374cb 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -1093,7 +1093,7 @@
checkSelectionChanged();
}
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ notifyAccessibilitySubtreeChanged();
}
/**
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index 92bfd56..af01a3e 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -132,7 +132,7 @@
if (mChecked != checked) {
mChecked = checked;
refreshDrawableState();
- notifyViewAccessibilityStateChangedIfNeeded(
+ notifyAccessibilityStateChanged(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 0762b15..e57f153 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -158,7 +158,7 @@
mCheckedFromResource = false;
mChecked = checked;
refreshDrawableState();
- notifyViewAccessibilityStateChangedIfNeeded(
+ notifyAccessibilityStateChanged(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
// Avoid infinite recursions if setChecked() is called from a listener
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 05d18d1..7bb0db1 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2095,14 +2095,7 @@
if (!(mTextView.getText() instanceof Spannable)) {
return;
}
- Spannable text = (Spannable) mTextView.getText();
stopTextActionMode();
- if (mTextView.isTextSelectable()) {
- Selection.setSelection((Spannable) text, link.getStart(), link.getEnd());
- } else {
- //TODO: Nonselectable text
- }
-
getSelectionActionModeHelper().startLinkActionModeAsync(link);
}
@@ -2179,7 +2172,8 @@
return false;
}
- if (!checkField() || !mTextView.hasSelection()) {
+ if (actionMode != TextActionMode.TEXT_LINK
+ && (!checkField() || !mTextView.hasSelection())) {
return false;
}
@@ -3679,6 +3673,8 @@
mIsShowingUp = true;
super.show();
}
+
+ mSuggestionListView.setVisibility(mNumberOfSuggestions == 0 ? View.GONE : View.VISIBLE);
}
@Override
@@ -4012,7 +4008,6 @@
if (isValidAssistMenuItem(
textClassification.getIcon(),
textClassification.getLabel(),
- textClassification.getOnClickListener(),
textClassification.getIntent())) {
final MenuItem item = menu.add(
TextView.ID_ASSIST, TextView.ID_ASSIST, MENU_ITEM_ORDER_ASSIST,
@@ -4020,14 +4015,15 @@
.setIcon(textClassification.getIcon())
.setIntent(textClassification.getIntent());
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
- mAssistClickHandlers.put(item, textClassification.getOnClickListener());
+ mAssistClickHandlers.put(
+ item, TextClassification.createStartActivityOnClickListener(
+ mTextView.getContext(), textClassification.getIntent()));
}
final int count = textClassification.getSecondaryActionsCount();
for (int i = 0; i < count; i++) {
if (!isValidAssistMenuItem(
textClassification.getSecondaryIcon(i),
textClassification.getSecondaryLabel(i),
- textClassification.getSecondaryOnClickListener(i),
textClassification.getSecondaryIntent(i))) {
continue;
}
@@ -4038,7 +4034,9 @@
.setIcon(textClassification.getSecondaryIcon(i))
.setIntent(textClassification.getSecondaryIntent(i));
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
- mAssistClickHandlers.put(item, textClassification.getSecondaryOnClickListener(i));
+ mAssistClickHandlers.put(item,
+ TextClassification.createStartActivityOnClickListener(
+ mTextView.getContext(), textClassification.getSecondaryIntent(i)));
}
}
@@ -4054,10 +4052,9 @@
}
}
- private boolean isValidAssistMenuItem(
- Drawable icon, CharSequence label, OnClickListener onClick, Intent intent) {
+ private boolean isValidAssistMenuItem(Drawable icon, CharSequence label, Intent intent) {
final boolean hasUi = icon != null || !TextUtils.isEmpty(label);
- final boolean hasAction = onClick != null || isSupportedIntent(intent);
+ final boolean hasAction = isSupportedIntent(intent);
return hasUi && hasAction;
}
@@ -4632,7 +4629,7 @@
return 0;
}
- protected final void showMagnifier() {
+ protected final void showMagnifier(@NonNull final MotionEvent event) {
if (mMagnifier == null) {
return;
}
@@ -4658,9 +4655,10 @@
final Layout layout = mTextView.getLayout();
final int lineNumber = layout.getLineForOffset(offset);
- // Horizontally snap to character offset.
- final float xPosInView = getHorizontal(mTextView.getLayout(), offset)
- + mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
+ // Horizontally move the magnifier smoothly.
+ final int[] textViewLocationOnScreen = new int[2];
+ mTextView.getLocationOnScreen(textViewLocationOnScreen);
+ final float xPosInView = event.getRawX() - textViewLocationOnScreen[0];
// Vertically snap to middle of current line.
final float yPosInView = (mTextView.getLayout().getLineTop(lineNumber)
+ mTextView.getLayout().getLineBottom(lineNumber)) / 2.0f
@@ -4855,11 +4853,11 @@
case MotionEvent.ACTION_DOWN:
mDownPositionX = ev.getRawX();
mDownPositionY = ev.getRawY();
- showMagnifier();
+ showMagnifier(ev);
break;
case MotionEvent.ACTION_MOVE:
- showMagnifier();
+ showMagnifier(ev);
break;
case MotionEvent.ACTION_UP:
@@ -5213,11 +5211,11 @@
// re-engages the handle.
mTouchWordDelta = 0.0f;
mPrevX = UNSET_X_VALUE;
- showMagnifier();
+ showMagnifier(event);
break;
case MotionEvent.ACTION_MOVE:
- showMagnifier();
+ showMagnifier(event);
break;
case MotionEvent.ACTION_UP:
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 26dfcc2..310b170 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -32,6 +32,7 @@
import android.view.Surface;
import android.view.SurfaceView;
import android.view.View;
+import android.view.ViewParent;
import com.android.internal.util.Preconditions;
@@ -44,6 +45,8 @@
private static final int NONEXISTENT_PREVIOUS_CONFIG_VALUE = -1;
// The view to which this magnifier is attached.
private final View mView;
+ // The coordinates of the view in the surface.
+ private final int[] mViewCoordinatesInSurface;
// The window containing the magnifier.
private final PopupWindow mWindow;
// The center coordinates of the window containing the magnifier.
@@ -87,6 +90,8 @@
com.android.internal.R.dimen.magnifier_height);
mZoomScale = context.getResources().getFloat(
com.android.internal.R.dimen.magnifier_zoom_scale);
+ // The view's surface coordinates will not be updated until the magnifier is first shown.
+ mViewCoordinatesInSurface = new int[2];
mWindow = new PopupWindow(context);
mWindow.setContentView(content);
@@ -120,9 +125,34 @@
configureCoordinates(xPosInView, yPosInView);
// Clamp startX value to avoid distorting the rendering of the magnifier content.
- final int startX = Math.max(0, Math.min(
+ // For this, we compute:
+ // - zeroScrollXInSurface: this is the start x of mView, where this is not masked by a
+ // potential scrolling container. For example, if mView is a
+ // TextView contained in a HorizontalScrollView,
+ // mViewCoordinatesInSurface will reflect the surface position of
+ // the first text character, rather than the position of the first
+ // visible one. Therefore, we need to add back the amount of
+ // scrolling from the parent containers.
+ // - actualWidth: similarly, the width of a View will be larger than its actually visible
+ // width when it is contained in a scrolling container. We need to use
+ // the minimum width of a scrolling container which contains this view.
+ int zeroScrollXInSurface = mViewCoordinatesInSurface[0];
+ int actualWidth = mView.getWidth();
+ ViewParent viewParent = mView.getParent();
+ while (viewParent instanceof View) {
+ final View container = (View) viewParent;
+ if (container.canScrollHorizontally(-1 /* left scroll */)
+ || container.canScrollHorizontally(1 /* right scroll */)) {
+ zeroScrollXInSurface += container.getScrollX();
+ actualWidth = Math.min(actualWidth, container.getWidth()
+ - container.getPaddingLeft() - container.getPaddingRight());
+ }
+ viewParent = viewParent.getParent();
+ }
+
+ final int startX = Math.max(zeroScrollXInSurface, Math.min(
mCenterZoomCoords.x - mBitmap.getWidth() / 2,
- mView.getWidth() - mBitmap.getWidth()));
+ zeroScrollXInSurface + actualWidth - mBitmap.getWidth()));
final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2;
if (xPosInView != mPrevPosInView.x || yPosInView != mPrevPosInView.y) {
@@ -169,10 +199,9 @@
posX = xPosInView;
posY = yPosInView;
} else {
- final int[] coordinatesInSurface = new int[2];
- mView.getLocationInSurface(coordinatesInSurface);
- posX = xPosInView + coordinatesInSurface[0];
- posY = yPosInView + coordinatesInSurface[1];
+ mView.getLocationInSurface(mViewCoordinatesInSurface);
+ posX = xPosInView + mViewCoordinatesInSurface[0];
+ posY = yPosInView + mViewCoordinatesInSurface[1];
}
mCenterZoomCoords.x = Math.round(posX);
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 2c6466c..3bfa520 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -235,10 +235,13 @@
@Editor.TextActionMode int actionMode, @Nullable SelectionResult result) {
final CharSequence text = getText(mTextView);
if (result != null && text instanceof Spannable
- && (mTextView.isTextSelectable() || mTextView.isTextEditable())) {
+ && (mTextView.isTextSelectable()
+ || mTextView.isTextEditable()
+ || actionMode == Editor.TextActionMode.TEXT_LINK)) {
// Do not change the selection if TextClassifier should be dark launched.
if (!mTextView.getTextClassifier().getSettings().isDarkLaunch()) {
Selection.setSelection((Spannable) text, result.mStart, result.mEnd);
+ mTextView.invalidate();
}
mTextClassification = result.mClassification;
} else {
@@ -250,8 +253,17 @@
&& (mTextView.isTextSelectable() || mTextView.isTextEditable())) {
controller.show();
}
- if (result != null && actionMode == Editor.TextActionMode.SELECTION) {
- mSelectionTracker.onSmartSelection(result);
+ if (result != null) {
+ switch (actionMode) {
+ case Editor.TextActionMode.SELECTION:
+ mSelectionTracker.onSmartSelection(result);
+ break;
+ case Editor.TextActionMode.TEXT_LINK:
+ mSelectionTracker.onLinkSelected(result);
+ break;
+ default:
+ break;
+ }
}
}
mEditor.setRestartActionModeOnNextRefresh(false);
@@ -486,12 +498,24 @@
* Called when selection action mode is started and the results come from a classifier.
*/
public void onSmartSelection(SelectionResult result) {
+ onClassifiedSelection(result);
+ mLogger.logSelectionModified(
+ result.mStart, result.mEnd, result.mClassification, result.mSelection);
+ }
+
+ /**
+ * Called when link action mode is started and the classification comes from a classifier.
+ */
+ public void onLinkSelected(SelectionResult result) {
+ onClassifiedSelection(result);
+ // TODO: log (b/70246800)
+ }
+
+ private void onClassifiedSelection(SelectionResult result) {
if (isSelectionStarted()) {
mSelectionStart = result.mStart;
mSelectionEnd = result.mEnd;
mAllowReset = mSelectionStart != mOriginalStart || mSelectionEnd != mOriginalEnd;
- mLogger.logSelectionModified(
- result.mStart, result.mEnd, result.mClassification, result.mSelection);
}
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index cae2d7d9..dac6c02 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -27,8 +27,10 @@
import android.annotation.DrawableRes;
import android.annotation.FloatRange;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.Px;
import android.annotation.Size;
import android.annotation.StringRes;
import android.annotation.StyleRes;
@@ -52,6 +54,7 @@
import android.graphics.Canvas;
import android.graphics.Insets;
import android.graphics.Paint;
+import android.graphics.Paint.FontMetricsInt;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.Rect;
@@ -77,8 +80,8 @@
import android.text.InputFilter;
import android.text.InputType;
import android.text.Layout;
+import android.text.MeasuredText;
import android.text.ParcelableSpan;
-import android.text.PremeasuredText;
import android.text.Selection;
import android.text.SpanWatcher;
import android.text.Spannable;
@@ -924,6 +927,9 @@
int inputType = EditorInfo.TYPE_NULL;
a = theme.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.TextView, defStyleAttr, defStyleRes);
+ int firstBaselineToTopHeight = -1;
+ int lastBaselineToBottomHeight = -1;
+ int lineHeight = -1;
readTextAppearance(context, a, attributes, true /* styleArray */);
@@ -1249,6 +1255,18 @@
case com.android.internal.R.styleable.TextView_justificationMode:
mJustificationMode = a.getInt(attr, Layout.JUSTIFICATION_MODE_NONE);
break;
+
+ case com.android.internal.R.styleable.TextView_firstBaselineToTopHeight:
+ firstBaselineToTopHeight = a.getDimensionPixelSize(attr, -1);
+ break;
+
+ case com.android.internal.R.styleable.TextView_lastBaselineToBottomHeight:
+ lastBaselineToBottomHeight = a.getDimensionPixelSize(attr, -1);
+ break;
+
+ case com.android.internal.R.styleable.TextView_lineHeight:
+ lineHeight = a.getDimensionPixelSize(attr, -1);
+ break;
}
}
@@ -1563,6 +1581,16 @@
} else {
mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_NONE;
}
+
+ if (firstBaselineToTopHeight >= 0) {
+ setFirstBaselineToTopHeight(firstBaselineToTopHeight);
+ }
+ if (lastBaselineToBottomHeight >= 0) {
+ setLastBaselineToBottomHeight(lastBaselineToBottomHeight);
+ }
+ if (lineHeight >= 0) {
+ setLineHeight(lineHeight);
+ }
}
/**
@@ -2365,7 +2393,7 @@
setText(mText);
if (hasPasswordTransformationMethod()) {
- notifyViewAccessibilityStateChangedIfNeeded(
+ notifyAccessibilityStateChanged(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
@@ -3165,6 +3193,12 @@
}
}
+ /**
+ * @inheritDoc
+ *
+ * @see #setFirstBaselineToTopHeight(int)
+ * @see #setLastBaselineToBottomHeight(int)
+ */
@Override
public void setPadding(int left, int top, int right, int bottom) {
if (left != mPaddingLeft
@@ -3179,6 +3213,12 @@
invalidate();
}
+ /**
+ * @inheritDoc
+ *
+ * @see #setFirstBaselineToTopHeight(int)
+ * @see #setLastBaselineToBottomHeight(int)
+ */
@Override
public void setPaddingRelative(int start, int top, int end, int bottom) {
if (start != getPaddingStart()
@@ -3194,6 +3234,97 @@
}
/**
+ * Updates the top padding of the TextView so that {@code firstBaselineToTopHeight} is
+ * equal to the distance between the firt text baseline and the top of this TextView.
+ * <strong>Note</strong> that if {@code FontMetrics.top} or {@code FontMetrics.ascent} was
+ * already greater than {@code firstBaselineToTopHeight}, the top padding is not updated.
+ *
+ * @param firstBaselineToTopHeight distance between first baseline to top of the container
+ * in pixels
+ *
+ * @see #getFirstBaselineToTopHeight()
+ * @see #setPadding(int, int, int, int)
+ * @see #setPaddingRelative(int, int, int, int)
+ *
+ * @attr ref android.R.styleable#TextView_firstBaselineToTopHeight
+ */
+ public void setFirstBaselineToTopHeight(@Px @IntRange(from = 0) int firstBaselineToTopHeight) {
+ Preconditions.checkArgumentNonnegative(firstBaselineToTopHeight);
+
+ final FontMetricsInt fontMetrics = getPaint().getFontMetricsInt();
+ final int fontMetricsTop;
+ if (getIncludeFontPadding()) {
+ fontMetricsTop = fontMetrics.top;
+ } else {
+ fontMetricsTop = fontMetrics.ascent;
+ }
+
+ // TODO: Decide if we want to ignore density ratio (i.e. when the user changes font size
+ // in settings). At the moment, we don't.
+
+ if (firstBaselineToTopHeight > Math.abs(fontMetricsTop)) {
+ final int paddingTop = firstBaselineToTopHeight - (-fontMetricsTop);
+ setPadding(getPaddingLeft(), paddingTop, getPaddingRight(), getPaddingBottom());
+ }
+ }
+
+ /**
+ * Updates the bottom padding of the TextView so that {@code lastBaselineToBottomHeight} is
+ * equal to the distance between the last text baseline and the bottom of this TextView.
+ * <strong>Note</strong> that if {@code FontMetrics.bottom} or {@code FontMetrics.descent} was
+ * already greater than {@code lastBaselineToBottomHeight}, the bottom padding is not updated.
+ *
+ * @param lastBaselineToBottomHeight distance between last baseline to bottom of the container
+ * in pixels
+ *
+ * @see #getLastBaselineToBottomHeight()
+ * @see #setPadding(int, int, int, int)
+ * @see #setPaddingRelative(int, int, int, int)
+ *
+ * @attr ref android.R.styleable#TextView_lastBaselineToBottomHeight
+ */
+ public void setLastBaselineToBottomHeight(
+ @Px @IntRange(from = 0) int lastBaselineToBottomHeight) {
+ Preconditions.checkArgumentNonnegative(lastBaselineToBottomHeight);
+
+ final FontMetricsInt fontMetrics = getPaint().getFontMetricsInt();
+ final int fontMetricsBottom;
+ if (getIncludeFontPadding()) {
+ fontMetricsBottom = fontMetrics.bottom;
+ } else {
+ fontMetricsBottom = fontMetrics.descent;
+ }
+
+ // TODO: Decide if we want to ignore density ratio (i.e. when the user changes font size
+ // in settings). At the moment, we don't.
+
+ if (lastBaselineToBottomHeight > Math.abs(fontMetricsBottom)) {
+ final int paddingBottom = lastBaselineToBottomHeight - fontMetricsBottom;
+ setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), paddingBottom);
+ }
+ }
+
+ /**
+ * Returns the distance between the first text baseline and the top of this TextView.
+ *
+ * @see #setFirstBaselineToTopHeight(int)
+ * @attr ref android.R.styleable#TextView_firstBaselineToTopHeight
+ */
+ public int getFirstBaselineToTopHeight() {
+ return getPaddingTop() - getPaint().getFontMetricsInt().top;
+ }
+
+ /**
+ * Returns the distance between the last text baseline and the bottom of this TextView.
+ *
+ * @see #setLastBaselineToBottomHeight(int)
+ * @attr ref android.R.styleable#TextView_lastBaselineToBottomHeight
+ */
+ public int getLastBaselineToBottomHeight() {
+ return getPaddingBottom() + getPaint().getFontMetricsInt().bottom;
+ }
+
+ /**
* Gets the autolink mask of the text. See {@link
* android.text.util.Linkify#ALL Linkify.ALL} and peers for
* possible values.
@@ -3756,6 +3887,7 @@
*
* @param elegant set the paint's elegant metrics flag.
*
+ * @see #isElegantTextHeight()
* @see Paint#isElegantTextHeight()
*
* @attr ref android.R.styleable#TextView_elegantTextHeight
@@ -4974,6 +5106,28 @@
}
/**
+ * Sets an explicit line height for this TextView. This is equivalent to the vertical distance
+ * between subsequent baselines in the TextView.
+ *
+ * @param lineHeight the line height in pixels
+ *
+ * @see #setLineSpacing(float, float)
+ * @see #getLineSpacing()
+ *
+ * @attr ref android.R.styleable#TextView_lineHeight
+ */
+ public void setLineHeight(@Px @IntRange(from = 0) int lineHeight) {
+ Preconditions.checkArgumentNonnegative(lineHeight);
+
+ final int fontHeight = getPaint().getFontMetricsInt(null);
+ // Make sure we don't setLineSpacing if it's not needed to avoid unnecessary redraw.
+ if (lineHeight != fontHeight) {
+ // Set lineSpacingExtra by the difference of lineSpacing with lineHeight
+ setLineSpacing(lineHeight - fontHeight, 1f);
+ }
+ }
+
+ /**
* Convenience method to append the specified text to the TextView's
* display buffer, upgrading it to {@link android.widget.TextView.BufferType#EDITABLE}
* if it was not already editable.
@@ -5393,7 +5547,7 @@
if (imm != null) imm.restartInput(this);
} else if (type == BufferType.SPANNABLE || mMovement != null) {
text = mSpannableFactory.newSpannable(text);
- } else if (!(text instanceof PremeasuredText || text instanceof CharWrapper)) {
+ } else if (!(text instanceof MeasuredText || text instanceof CharWrapper)) {
text = TextUtils.stringOrSpannedString(text);
}
@@ -5476,7 +5630,7 @@
sendOnTextChanged(text, 0, oldlen, textLength);
onTextChanged(text, 0, oldlen, textLength);
- notifyViewAccessibilityStateChangedIfNeeded(AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
+ notifyAccessibilityStateChanged(AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
if (needEditableForNotification) {
sendAfterTextChanged((Editable) text);
@@ -6210,7 +6364,7 @@
public void setError(CharSequence error, Drawable icon) {
createEditorIfNeeded();
mEditor.setError(error, icon);
- notifyViewAccessibilityStateChangedIfNeeded(
+ notifyAccessibilityStateChanged(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
@@ -9125,8 +9279,7 @@
/**
*
- * Checks whether the transformation method applied to this TextView is set to ALL CAPS. This
- * settings is internally ignored if this field is editable or selectable.
+ * Checks whether the transformation method applied to this TextView is set to ALL CAPS.
* @return Whether the current transformation method is for ALL CAPS.
*
* @see #setAllCaps(boolean)
@@ -11011,6 +11164,12 @@
return true;
case ID_COPY:
+ // For link action mode in a non-selectable/non-focusable TextView,
+ // make sure that we set the appropriate min/max.
+ final int selStart = getSelectionStart();
+ final int selEnd = getSelectionEnd();
+ min = Math.max(0, Math.min(selStart, selEnd));
+ max = Math.max(0, Math.max(selStart, selEnd));
final ClipData copyData = ClipData.newPlainText(null, getTransformedText(min, max));
if (setPrimaryClip(copyData)) {
stopTextActionMode();
@@ -11233,11 +11392,9 @@
*/
public boolean requestActionMode(@NonNull TextLinks.TextLink link) {
Preconditions.checkNotNull(link);
- if (mEditor != null) {
- mEditor.startLinkActionModeAsync(link);
- return true;
- }
- return false;
+ createEditorIfNeeded();
+ mEditor.startLinkActionModeAsync(link);
+ return true;
}
/**
* @hide
diff --git a/core/java/android/widget/VideoView2.java b/core/java/android/widget/VideoView2.java
new file mode 100644
index 0000000..310a7bb
--- /dev/null
+++ b/core/java/android/widget/VideoView2.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.media.AudioAttributes;
+import android.media.MediaPlayer;
+import android.media.update.ApiLoader;
+import android.media.update.VideoView2Provider;
+import android.media.update.ViewProvider;
+import android.net.Uri;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Map;
+
+/**
+ * TODO PUBLIC API
+ * @hide
+ */
+public class VideoView2 extends FrameLayout {
+ @IntDef({
+ VIEW_TYPE_TEXTUREVIEW,
+ VIEW_TYPE_SURFACEVIEW
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ViewType {}
+ public static final int VIEW_TYPE_SURFACEVIEW = 1;
+ public static final int VIEW_TYPE_TEXTUREVIEW = 2;
+
+ private final VideoView2Provider mProvider;
+
+ /**
+ * @hide
+ */
+ public VideoView2(@NonNull Context context) {
+ this(context, null);
+ }
+
+ /**
+ * @hide
+ */
+ public VideoView2(@NonNull Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ /**
+ * @hide
+ */
+ public VideoView2(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ /**
+ * @hide
+ */
+ public VideoView2(
+ @NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ mProvider = ApiLoader.getProvider(context).createVideoView2(this, new SuperProvider());
+ }
+
+ /**
+ * @hide
+ */
+ public VideoView2Provider getProvider() {
+ return mProvider;
+ }
+
+ /**
+ * @hide
+ */
+ public void start() {
+ mProvider.start_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public void pause() {
+ mProvider.pause_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public int getDuration() {
+ return mProvider.getDuration_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public int getCurrentPosition() {
+ return mProvider.getCurrentPosition_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public void seekTo(int msec) {
+ mProvider.seekTo_impl(msec);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean isPlaying() {
+ return mProvider.isPlaying_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public int getBufferPercentage() {
+ return mProvider.getBufferPercentage_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public int getAudioSessionId() {
+ return mProvider.getAudioSessionId_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public void showSubtitle() {
+ mProvider.showSubtitle_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public void hideSubtitle() {
+ mProvider.hideSubtitle_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public void setAudioFocusRequest(int focusGain) {
+ mProvider.setAudioFocusRequest_impl(focusGain);
+ }
+
+ /**
+ * @hide
+ */
+ public void setAudioAttributes(@NonNull AudioAttributes attributes) {
+ mProvider.setAudioAttributes_impl(attributes);
+ }
+
+ /**
+ * @hide
+ */
+ public void setVideoPath(String path) {
+ mProvider.setVideoPath_impl(path);
+ }
+
+ /**
+ * @hide
+ */
+ public void setVideoURI(Uri uri) {
+ mProvider.setVideoURI_impl(uri);
+ }
+
+ /**
+ * @hide
+ */
+ public void setVideoURI(Uri uri, Map<String, String> headers) {
+ mProvider.setVideoURI_impl(uri, headers);
+ }
+
+ /**
+ * @hide
+ */
+ public void setMediaController2(MediaController2 controllerView) {
+ mProvider.setMediaController2_impl(controllerView);
+ }
+
+ /**
+ * @hide
+ */
+ public void setViewType(@ViewType int viewType) {
+ mProvider.setViewType_impl(viewType);
+ }
+
+ /**
+ * @hide
+ */
+ @ViewType
+ public int getViewType() {
+ return mProvider.getViewType_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public void stopPlayback() {
+ mProvider.stopPlayback_impl();
+ }
+
+ /**
+ * @hide
+ */
+ public void setOnPreparedListener(MediaPlayer.OnPreparedListener l) {
+ mProvider.setOnPreparedListener_impl(l);
+ }
+
+ /**
+ * @hide
+ */
+ public void setOnCompletionListener(MediaPlayer.OnCompletionListener l) {
+ mProvider.setOnCompletionListener_impl(l);
+ }
+
+ /**
+ * @hide
+ */
+ public void setOnErrorListener(MediaPlayer.OnErrorListener l) {
+ mProvider.setOnErrorListener_impl(l);
+ }
+
+ /**
+ * @hide
+ */
+ public void setOnInfoListener(MediaPlayer.OnInfoListener l) {
+ mProvider.setOnInfoListener_impl(l);
+ }
+
+ /**
+ * @hide
+ */
+ public void setOnViewTypeChangedListener(OnViewTypeChangedListener l) {
+ mProvider.setOnViewTypeChangedListener_impl(l);
+ }
+
+ /**
+ * @hide
+ */
+ public interface OnViewTypeChangedListener {
+ /**
+ * @hide
+ */
+ void onViewTypeChanged(@ViewType int viewType);
+ }
+
+ @Override
+ public CharSequence getAccessibilityClassName() {
+ return mProvider.getAccessibilityClassName_impl();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ return mProvider.onTouchEvent_impl(ev);
+ }
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent ev) {
+ return mProvider.onTrackballEvent_impl(ev);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ return mProvider.onKeyDown_impl(keyCode, event);
+ }
+
+ @Override
+ public void onFinishInflate() {
+ mProvider.onFinishInflate_impl();
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ return mProvider.dispatchKeyEvent_impl(event);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ mProvider.setEnabled_impl(enabled);
+ }
+
+ private class SuperProvider implements ViewProvider {
+ @Override
+ public void onAttachedToWindow_impl() {
+ VideoView2.super.onAttachedToWindow();
+ }
+
+ @Override
+ public void onDetachedFromWindow_impl() {
+ VideoView2.super.onDetachedFromWindow();
+ }
+
+ @Override
+ public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) {
+ VideoView2.super.onLayout(changed, left, top, right, bottom);
+ }
+
+ @Override
+ public void draw_impl(Canvas canvas) {
+ VideoView2.super.draw(canvas);
+ }
+
+ @Override
+ public CharSequence getAccessibilityClassName_impl() {
+ return VideoView2.super.getAccessibilityClassName();
+ }
+
+ @Override
+ public boolean onTouchEvent_impl(MotionEvent ev) {
+ return VideoView2.super.onTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onTrackballEvent_impl(MotionEvent ev) {
+ return VideoView2.super.onTrackballEvent(ev);
+ }
+
+ @Override
+ public boolean onKeyDown_impl(int keyCode, KeyEvent event) {
+ return VideoView2.super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public void onFinishInflate_impl() {
+ VideoView2.super.onFinishInflate();
+ }
+
+ @Override
+ public boolean dispatchKeyEvent_impl(KeyEvent event) {
+ return VideoView2.super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ public void setEnabled_impl(boolean enabled) {
+ VideoView2.super.setEnabled(enabled);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/app/HarmfulAppWarningActivity.java b/core/java/com/android/internal/app/HarmfulAppWarningActivity.java
new file mode 100644
index 0000000..042da36
--- /dev/null
+++ b/core/java/com/android/internal/app/HarmfulAppWarningActivity.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.app;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.util.Log;
+import com.android.internal.R;
+
+/**
+ * This dialog is shown to the user before an activity in a harmful app is launched.
+ *
+ * See {@code PackageManager.setHarmfulAppInfo} for more info.
+ */
+public class HarmfulAppWarningActivity extends AlertActivity implements
+ DialogInterface.OnClickListener {
+ private static final String TAG = "HarmfulAppWarningActivity";
+
+ private static final String EXTRA_HARMFUL_APP_WARNING = "harmful_app_warning";
+
+ private String mPackageName;
+ private String mHarmfulAppWarning;
+ private IntentSender mTarget;
+
+ // [b/63909431] STOPSHIP replace placeholder UI with final Harmful App Warning UI
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent intent = getIntent();
+ mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+ mTarget = intent.getParcelableExtra(Intent.EXTRA_INTENT);
+ mHarmfulAppWarning = intent.getStringExtra(EXTRA_HARMFUL_APP_WARNING);
+
+ if (mPackageName == null || mTarget == null || mHarmfulAppWarning == null) {
+ Log.wtf(TAG, "Invalid intent: " + intent.toString());
+ finish();
+ }
+
+ AlertController.AlertParams p = mAlertParams;
+ p.mTitle = getString(R.string.harmful_app_warning_title);
+ p.mMessage = mHarmfulAppWarning;
+ p.mPositiveButtonText = getString(R.string.harmful_app_warning_launch_anyway);
+ p.mPositiveButtonListener = this;
+ p.mNegativeButtonText = getString(R.string.harmful_app_warning_uninstall);
+ p.mNegativeButtonListener = this;
+
+ mAlert.installContent(mAlertParams);
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case DialogInterface.BUTTON_POSITIVE:
+ getPackageManager().setHarmfulAppWarning(mPackageName, null);
+
+ IntentSender target = getIntent().getParcelableExtra(Intent.EXTRA_INTENT);
+ try {
+ startIntentSenderForResult(target, -1, null, 0, 0, 0);
+ } catch (IntentSender.SendIntentException e) {
+ // ignore..
+ }
+ finish();
+ break;
+ case DialogInterface.BUTTON_NEGATIVE:
+ getPackageManager().deletePackage(mPackageName, null, 0);
+ finish();
+ break;
+ }
+ }
+
+ public static Intent createHarmfulAppWarningIntent(Context context, String targetPackageName,
+ IntentSender target, CharSequence harmfulAppWarning) {
+ Intent intent = new Intent();
+ intent.setClass(context, HarmfulAppWarningActivity.class);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, targetPackageName);
+ intent.putExtra(Intent.EXTRA_INTENT, target);
+ intent.putExtra(EXTRA_HARMFUL_APP_WARNING, harmfulAppWarning);
+ return intent;
+ }
+}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 5d4bccc..388180d 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -123,8 +123,6 @@
void noteWifiScanStoppedFromSource(in WorkSource ws);
void noteWifiBatchedScanStartedFromSource(in WorkSource ws, int csph);
void noteWifiBatchedScanStoppedFromSource(in WorkSource ws);
- void noteWifiMulticastEnabledFromSource(in WorkSource ws);
- void noteWifiMulticastDisabledFromSource(in WorkSource ws);
void noteWifiRadioPowerState(int powerState, long timestampNs, int uid);
void noteNetworkInterfaceType(String iface, int type);
void noteNetworkStatsEnabled();
diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
index 2eadaf3..902c8c1 100644
--- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java
+++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
@@ -111,7 +111,7 @@
@Override
public void onClick(DialogInterface dialog, int which) {
if (mReason == UNLAUNCHABLE_REASON_QUIET_MODE && which == DialogInterface.BUTTON_POSITIVE) {
- UserManager.get(this).trySetQuietModeEnabled(false, UserHandle.of(mUserId), mTarget);
+ UserManager.get(this).requestQuietModeEnabled(false, UserHandle.of(mUserId), mTarget);
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index b8ff9e4..ac5dbc4 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -4062,7 +4062,6 @@
public void noteWakupAlarmLocked(String packageName, int uid, WorkSource workSource,
String tag) {
-
if (workSource != null) {
for (int i = 0; i < workSource.size(); ++i) {
uid = workSource.get(i);
@@ -4073,8 +4072,8 @@
workSourceName != null ? workSourceName : packageName);
pkg.noteWakeupAlarmLocked(tag);
}
-
- StatsLog.write(StatsLog.WAKEUP_ALARM_OCCURRED, uid, tag);
+ StatsLog.write_non_chained(StatsLog.WAKEUP_ALARM_OCCURRED, workSource.get(i),
+ workSource.getName(i), tag);
}
ArrayList<WorkChain> workChains = workSource.getWorkChains();
@@ -4087,9 +4086,7 @@
BatteryStatsImpl.Uid.Pkg pkg = getPackageStatsLocked(uid, packageName);
pkg.noteWakeupAlarmLocked(tag);
}
-
- // TODO(statsd): Log the full attribution chain here once it's available
- StatsLog.write(StatsLog.WAKEUP_ALARM_OCCURRED, uid, tag);
+ StatsLog.write(StatsLog.WAKEUP_ALARM_OCCURRED, wc.getUids(), wc.getTags(), tag);
}
}
} else {
@@ -4097,7 +4094,7 @@
BatteryStatsImpl.Uid.Pkg pkg = getPackageStatsLocked(uid, packageName);
pkg.noteWakeupAlarmLocked(tag);
}
- StatsLog.write(StatsLog.WAKEUP_ALARM_OCCURRED, uid, tag);
+ StatsLog.write_non_chained(StatsLog.WAKEUP_ALARM_OCCURRED, uid, null, tag);
}
}
@@ -4221,6 +4218,9 @@
if (wc != null) {
StatsLog.write(
StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(), type, name, 1);
+ } else {
+ StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null, type, name,
+ 1);
}
}
}
@@ -4261,6 +4261,9 @@
if (wc != null) {
StatsLog.write(
StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(), type, name, 0);
+ } else {
+ StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null, type, name,
+ 0);
}
}
}
@@ -4354,9 +4357,11 @@
}
public void noteLongPartialWakelockStart(String name, String historyName, int uid) {
+ StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
+ uid, null, name, historyName, 1);
+
uid = mapUid(uid);
noteLongPartialWakeLockStartInternal(name, historyName, uid);
- StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, uid, name, historyName, 1);
}
public void noteLongPartialWakelockStartFromSource(String name, String historyName,
@@ -4365,7 +4370,8 @@
for (int i = 0; i < N; ++i) {
final int uid = mapUid(workSource.get(i));
noteLongPartialWakeLockStartInternal(name, historyName, uid);
- StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, uid, name, historyName, 1);
+ StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
+ workSource.get(i), workSource.getName(i), name, historyName, 1);
}
final ArrayList<WorkChain> workChains = workSource.getWorkChains();
@@ -4375,9 +4381,8 @@
final int uid = workChain.getAttributionUid();
noteLongPartialWakeLockStartInternal(name, historyName, uid);
- // TODO(statsd): the Log WorkChain to statds, and not just the uid.
- StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, uid, name, historyName,
- 1);
+ StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
+ workChain.getUids(), workChain.getTags(), name, historyName, 1);
}
}
}
@@ -4397,9 +4402,11 @@
}
public void noteLongPartialWakelockFinish(String name, String historyName, int uid) {
+ StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
+ uid, null, name, historyName, 0);
+
uid = mapUid(uid);
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
- StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, uid, name, historyName, 0);
}
public void noteLongPartialWakelockFinishFromSource(String name, String historyName,
@@ -4408,7 +4415,8 @@
for (int i = 0; i < N; ++i) {
final int uid = mapUid(workSource.get(i));
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
- StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, uid, name, historyName, 0);
+ StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
+ workSource.get(i), workSource.getName(i), name, historyName, 0);
}
final ArrayList<WorkChain> workChains = workSource.getWorkChains();
@@ -4416,12 +4424,9 @@
for (int i = 0; i < workChains.size(); ++i) {
final WorkChain workChain = workChains.get(i);
final int uid = workChain.getAttributionUid();
-
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
-
- // TODO(statsd): the Log WorkChain to statds, and not just the uid.
- StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, uid, name, historyName,
- 0);
+ StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
+ workChain.getUids(), workChain.getTags(), name, historyName, 0);
}
}
}
@@ -5388,10 +5393,19 @@
}
mBluetoothScanNesting++;
- // TODO(statsd): Log WorkChain here if it's non-null.
- StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, uid, 1);
- if (isUnoptimized) {
- StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, 1);
+ if (workChain != null) {
+ StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED,
+ workChain.getUids(), workChain.getTags(), 1);
+ if (isUnoptimized) {
+ StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
+ workChain.getUids(), workChain.getTags(), 1);
+ }
+ } else {
+ StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, 1);
+ if (isUnoptimized) {
+ StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null,
+ 1);
+ }
}
getUidStatsLocked(uid).noteBluetoothScanStartedLocked(elapsedRealtime, isUnoptimized);
@@ -5428,10 +5442,19 @@
mBluetoothScanTimer.stopRunningLocked(elapsedRealtime);
}
- // TODO(statsd): Log WorkChain here if it's non-null.
- StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, uid, 0);
- if (isUnoptimized) {
- StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, 0);
+ if (workChain != null) {
+ StatsLog.write(
+ StatsLog.BLE_SCAN_STATE_CHANGED, workChain.getUids(), workChain.getTags(), 0);
+ if (isUnoptimized) {
+ StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
+ workChain.getUids(), workChain.getTags(), 0);
+ }
+ } else {
+ StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, 0);
+ if (isUnoptimized) {
+ StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null,
+ 0);
+ }
}
getUidStatsLocked(uid).noteBluetoothScanStoppedLocked(elapsedRealtime, isUnoptimized);
@@ -5478,27 +5501,23 @@
BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
uid.noteResetBluetoothScanLocked(elapsedRealtime);
- StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, uid.getUid(), 0);
-
List<WorkChain> allWorkChains = uid.getAllBluetoothWorkChains();
if (allWorkChains != null) {
for (int j = 0; j < allWorkChains.size(); ++j) {
- // TODO(statsd) : Log the entire workchain here.
StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED,
- allWorkChains.get(j).getAttributionUid(), 0);
+ allWorkChains.get(j).getUids(),
+ allWorkChains.get(j).getTags(), 0);
}
-
allWorkChains.clear();
}
List<WorkChain> unoptimizedWorkChains = uid.getUnoptimizedBluetoothWorkChains();
if (unoptimizedWorkChains != null) {
for (int j = 0; j < unoptimizedWorkChains.size(); ++j) {
- // TODO(statsd) : Log the entire workchain here.
StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
- unoptimizedWorkChains.get(j).getAttributionUid(), 0);
+ unoptimizedWorkChains.get(j).getUids(),
+ unoptimizedWorkChains.get(j).getTags(), 0);
}
-
unoptimizedWorkChains.clear();
}
}
@@ -5510,7 +5529,8 @@
for (int i = 0; i < N; i++) {
int uid = mapUid(ws.get(i));
getUidStatsLocked(uid).noteBluetoothScanResultsLocked(numNewResults);
- StatsLog.write(StatsLog.BLE_SCAN_RESULT_RECEIVED, uid, numNewResults);
+ StatsLog.write_non_chained(StatsLog.BLE_SCAN_RESULT_RECEIVED, ws.get(i), ws.getName(i),
+ numNewResults);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -5519,8 +5539,8 @@
final WorkChain wc = workChains.get(i);
int uid = mapUid(wc.getAttributionUid());
getUidStatsLocked(uid).noteBluetoothScanResultsLocked(numNewResults);
- // TODO(statsd): Log the entire WorkChain here.
- StatsLog.write(StatsLog.BLE_SCAN_RESULT_RECEIVED, uid, numNewResults);
+ StatsLog.write(StatsLog.BLE_SCAN_RESULT_RECEIVED,
+ wc.getUids(), wc.getTags(), numNewResults);
}
}
}
@@ -5838,7 +5858,7 @@
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteFullWifiLockAcquiredLocked(uid);
- StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED, uid, 1);
+ StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), 1);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -5847,12 +5867,8 @@
final WorkChain workChain = workChains.get(i);
final int uid = mapUid(workChain.getAttributionUid());
noteFullWifiLockAcquiredLocked(uid);
-
- // TODO(statsd): Log workChain instead of uid here.
- if (DEBUG) {
- Slog.v(TAG, "noteFullWifiLockAcquiredFromSourceLocked: " + workChain);
- }
- StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED, uid, 1);
+ StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED,
+ workChain.getUids(), workChain.getTags(), 1);
}
}
}
@@ -5862,7 +5878,7 @@
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteFullWifiLockReleasedLocked(uid);
- StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED, uid, 0);
+ StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), 0);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -5871,12 +5887,8 @@
final WorkChain workChain = workChains.get(i);
final int uid = mapUid(workChain.getAttributionUid());
noteFullWifiLockReleasedLocked(uid);
-
- // TODO(statsd): Log workChain instead of uid here.
- if (DEBUG) {
- Slog.v(TAG, "noteFullWifiLockReleasedFromSourceLocked: " + workChain);
- }
- StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED, uid, 0);
+ StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED,
+ workChain.getUids(), workChain.getTags(), 0);
}
}
}
@@ -5886,7 +5898,8 @@
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteWifiScanStartedLocked(uid);
- StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED, uid, 1);
+ StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
+ 1);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -5895,12 +5908,8 @@
final WorkChain workChain = workChains.get(i);
final int uid = mapUid(workChain.getAttributionUid());
noteWifiScanStartedLocked(uid);
-
- // TODO(statsd): Log workChain instead of uid here.
- if (DEBUG) {
- Slog.v(TAG, "noteWifiScanStartedFromSourceLocked: " + workChain);
- }
- StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED, uid, 1);
+ StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED, workChain.getUids(),
+ workChain.getTags(), 1);
}
}
}
@@ -5910,7 +5919,8 @@
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteWifiScanStoppedLocked(uid);
- StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED, uid, 0);
+ StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
+ 0);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -5919,11 +5929,8 @@
final WorkChain workChain = workChains.get(i);
final int uid = mapUid(workChain.getAttributionUid());
noteWifiScanStoppedLocked(uid);
-
- if (DEBUG) {
- Slog.v(TAG, "noteWifiScanStoppedFromSourceLocked: " + workChain);
- }
- StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED, uid, 0);
+ StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED,
+ workChain.getUids(), workChain.getTags(), 0);
}
}
}
@@ -5956,34 +5963,6 @@
}
}
- public void noteWifiMulticastEnabledFromSourceLocked(WorkSource ws) {
- int N = ws.size();
- for (int i=0; i<N; i++) {
- noteWifiMulticastEnabledLocked(ws.get(i));
- }
-
- final List<WorkChain> workChains = ws.getWorkChains();
- if (workChains != null) {
- for (int i = 0; i < workChains.size(); ++i) {
- noteWifiMulticastEnabledLocked(workChains.get(i).getAttributionUid());
- }
- }
- }
-
- public void noteWifiMulticastDisabledFromSourceLocked(WorkSource ws) {
- int N = ws.size();
- for (int i=0; i<N; i++) {
- noteWifiMulticastDisabledLocked(ws.get(i));
- }
-
- final List<WorkChain> workChains = ws.getWorkChains();
- if (workChains != null) {
- for (int i = 0; i < workChains.size(); ++i) {
- noteWifiMulticastDisabledLocked(workChains.get(i).getAttributionUid());
- }
- }
- }
-
private static String[] includeInStringArray(String[] array, String str) {
if (ArrayUtils.indexOf(array, str) >= 0) {
return array;
@@ -6917,16 +6896,14 @@
public void noteAudioTurnedOnLocked(long elapsedRealtimeMs) {
createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- // TODO(statsd): Possibly use a worksource instead of a uid.
- StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, getUid(), 1);
+ StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 1);
}
public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) {
if (mAudioTurnedOnTimer != null) {
mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mAudioTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
- // TODO(statsd): Possibly use a worksource instead of a uid.
- StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, getUid(), 0);
+ StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 0);
}
}
}
@@ -6934,8 +6911,7 @@
public void noteResetAudioLocked(long elapsedRealtimeMs) {
if (mAudioTurnedOnTimer != null) {
mAudioTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- // TODO(statsd): Possibly use a worksource instead of a uid.
- StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, getUid(), 0);
+ StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 0);
}
}
@@ -6949,16 +6925,15 @@
public void noteVideoTurnedOnLocked(long elapsedRealtimeMs) {
createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- // TODO(statsd): Possibly use a worksource instead of a uid.
- StatsLog.write(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), 1);
+ StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null, 1);
}
public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) {
if (mVideoTurnedOnTimer != null) {
mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mVideoTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
- // TODO(statsd): Possibly use a worksource instead of a uid.
- StatsLog.write(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), 0);
+ StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(),
+ null, 0);
}
}
}
@@ -6966,8 +6941,8 @@
public void noteResetVideoLocked(long elapsedRealtimeMs) {
if (mVideoTurnedOnTimer != null) {
mVideoTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- // TODO(statsd): Possibly use a worksource instead of a uid.
- StatsLog.write(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), 0);
+ StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null,
+ 0);
}
}
@@ -6981,16 +6956,15 @@
public void noteFlashlightTurnedOnLocked(long elapsedRealtimeMs) {
createFlashlightTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- // TODO(statsd): Possibly use a worksource instead of a uid.
- StatsLog.write(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), 1);
+ StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,1);
}
public void noteFlashlightTurnedOffLocked(long elapsedRealtimeMs) {
if (mFlashlightTurnedOnTimer != null) {
mFlashlightTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mFlashlightTurnedOnTimer.isRunningLocked()) {
- // TODO(statsd): Possibly use a worksource instead of a uid.
- StatsLog.write(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), 0);
+ StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
+ 0);
}
}
}
@@ -6998,8 +6972,7 @@
public void noteResetFlashlightLocked(long elapsedRealtimeMs) {
if (mFlashlightTurnedOnTimer != null) {
mFlashlightTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- // TODO(statsd): Possibly use a worksource instead of a uid.
- StatsLog.write(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), 0);
+ StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null, 0);
}
}
@@ -7013,16 +6986,14 @@
public void noteCameraTurnedOnLocked(long elapsedRealtimeMs) {
createCameraTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- // TODO(statsd): Possibly use a worksource instead of a uid.
- StatsLog.write(StatsLog.CAMERA_STATE_CHANGED, getUid(), 1);
+ StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 1);
}
public void noteCameraTurnedOffLocked(long elapsedRealtimeMs) {
if (mCameraTurnedOnTimer != null) {
mCameraTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mCameraTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
- // TODO(statsd): Possibly use a worksource instead of a uid.
- StatsLog.write(StatsLog.CAMERA_STATE_CHANGED, getUid(), 0);
+ StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 0);
}
}
}
@@ -7030,8 +7001,7 @@
public void noteResetCameraLocked(long elapsedRealtimeMs) {
if (mCameraTurnedOnTimer != null) {
mCameraTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- // TODO(statsd): Possibly use a worksource instead of a uid.
- StatsLog.write(StatsLog.CAMERA_STATE_CHANGED, getUid(), 0);
+ StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 0);
}
}
@@ -9593,8 +9563,7 @@
DualTimer t = mSyncStats.startObject(name);
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
- // TODO(statsd): Possibly use a worksource instead of a uid.
- StatsLog.write(StatsLog.SYNC_STATE_CHANGED, getUid(), name, 1);
+ StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, 1);
}
}
@@ -9603,8 +9572,7 @@
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
if (!t.isRunningLocked()) { // only tell statsd if truly stopped
- // TODO(statsd): Possibly use a worksource instead of a uid.
- StatsLog.write(StatsLog.SYNC_STATE_CHANGED, getUid(), name, 0);
+ StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, 0);
}
}
}
@@ -9613,8 +9581,8 @@
DualTimer t = mJobStats.startObject(name);
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
- // TODO(statsd): Possibly use a worksource instead of a uid.
- StatsLog.write(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), name, 1);
+ StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
+ name, 1);
}
}
@@ -9623,8 +9591,8 @@
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
if (!t.isRunningLocked()) { // only tell statsd if truly stopped
- // TODO(statsd): Possibly use a worksource instead of a uid.
- StatsLog.write(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), name, 0);
+ StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
+ name, 0);
}
}
if (mBsi.mOnBatteryTimeBase.isRunning()) {
@@ -9735,11 +9703,11 @@
public void noteStartSensor(int sensor, long elapsedRealtimeMs) {
DualTimer t = getSensorTimerLocked(sensor, /* create= */ true);
t.startRunningLocked(elapsedRealtimeMs);
- // TODO(statsd): Possibly use a worksource instead of a uid.
if (sensor == Sensor.GPS) {
- StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, getUid(), 1);
+ StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, getUid(), null, 1);
} else {
- StatsLog.write(StatsLog.SENSOR_STATE_CHANGED, getUid(), sensor, 1);
+ StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null, sensor,
+ 1);
}
}
@@ -9749,11 +9717,12 @@
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
if (!t.isRunningLocked()) { // only tell statsd if truly stopped
- // TODO(statsd): Possibly use a worksource instead of a uid.
if (sensor == Sensor.GPS) {
- StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, getUid(), 0);
+ StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, getUid(), null,
+ 0);
} else {
- StatsLog.write(StatsLog.SENSOR_STATE_CHANGED, getUid(), sensor, 0);
+ StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null,
+ sensor, 0);
}
}
}
diff --git a/core/java/com/android/internal/os/KernelCpuSpeedReader.java b/core/java/com/android/internal/os/KernelCpuSpeedReader.java
index 4c0370c..98fea01 100644
--- a/core/java/com/android/internal/os/KernelCpuSpeedReader.java
+++ b/core/java/com/android/internal/os/KernelCpuSpeedReader.java
@@ -38,6 +38,7 @@
private static final String TAG = "KernelCpuSpeedReader";
private final String mProcFile;
+ private final int mNumSpeedSteps;
private final long[] mLastSpeedTimesMs;
private final long[] mDeltaSpeedTimesMs;
@@ -50,6 +51,7 @@
public KernelCpuSpeedReader(int cpuNumber, int numSpeedSteps) {
mProcFile = String.format("/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state",
cpuNumber);
+ mNumSpeedSteps = numSpeedSteps;
mLastSpeedTimesMs = new long[numSpeedSteps];
mDeltaSpeedTimesMs = new long[numSpeedSteps];
long jiffyHz = Os.sysconf(OsConstants._SC_CLK_TCK);
@@ -90,4 +92,31 @@
}
return mDeltaSpeedTimesMs;
}
+
+ /**
+ * @return The time (in milliseconds) spent at different cpu speeds. The values should be
+ * monotonically increasing, unless the cpu was hotplugged.
+ */
+ public long[] readAbsolute() {
+ StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads();
+ long[] speedTimeMs = new long[mNumSpeedSteps];
+ try (BufferedReader reader = new BufferedReader(new FileReader(mProcFile))) {
+ TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
+ String line;
+ int speedIndex = 0;
+ while (speedIndex < mNumSpeedSteps && (line = reader.readLine()) != null) {
+ splitter.setString(line);
+ splitter.next();
+ long time = Long.parseLong(splitter.next()) * mJiffyMillis;
+ speedTimeMs[speedIndex] = time;
+ speedIndex++;
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read cpu-freq: " + e.getMessage());
+ Arrays.fill(speedTimeMs, 0);
+ } finally {
+ StrictMode.setThreadPolicy(policy);
+ }
+ return speedTimeMs;
+ }
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index cbc63cf..079ba0b 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -67,6 +67,9 @@
private Zygote() {}
+ /** Called for some security initialization before any fork. */
+ native static void nativeSecurityInit();
+
/**
* Forks a new VM instance. The current VM must have been started
* with the -Xzygote flag. <b>NOTE: new instance keeps all
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index c5fe4cb..2671f29 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -30,7 +30,6 @@
import android.os.Environment;
import android.os.Process;
import android.os.RemoteException;
-import android.os.Seccomp;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
@@ -572,10 +571,12 @@
final String seInfo = null;
final String classLoaderContext =
getSystemServerClassLoaderContext(classPathForElement);
+ final int targetSdkVersion = 0; // SystemServer targets the system's SDK version
try {
installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
- uuid, classLoaderContext, seInfo, false /* downgrade */);
+ uuid, classLoaderContext, seInfo, false /* downgrade */,
+ targetSdkVersion);
} catch (RemoteException | ServiceSpecificException e) {
// Ignore (but log), we need this on the classpath for fallback mode.
Log.w(TAG, "Failed compiling classpath element for system server: "
@@ -779,12 +780,11 @@
// Zygote.
Trace.setTracingEnabled(false, 0);
+ Zygote.nativeSecurityInit();
+
// Zygote process unmounts root storage spaces.
Zygote.nativeUnmountStorageOnInit();
- // Set seccomp policy
- Seccomp.setPolicy();
-
ZygoteHooks.stopZygoteNoThreadCreation();
if (startSystemServer) {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 5fddfba..95bc352 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -101,7 +101,6 @@
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
@@ -194,8 +193,6 @@
// View added at runtime to draw under the status bar area
private View mStatusGuard;
- // View added at runtime to draw under the navigation bar area
- private View mNavigationGuard;
private final ColorViewState mStatusColorViewState =
new ColorViewState(STATUS_BAR_COLOR_VIEW_ATTRIBUTES);
@@ -1002,7 +999,6 @@
mFrameOffsets.set(insets.getSystemWindowInsets());
insets = updateColorViews(insets, true /* animate */);
insets = updateStatusGuard(insets);
- insets = updateNavigationGuard(insets);
if (getForeground() != null) {
drawableChanged();
}
@@ -1062,7 +1058,10 @@
WindowManager.LayoutParams attrs = mWindow.getAttributes();
int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
- if (!mWindow.mIsFloating) {
+ // IME is an exceptional floating window that requires color view.
+ final boolean isImeWindow =
+ mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+ if (!mWindow.mIsFloating || isImeWindow) {
boolean disallowAnimate = !isLaidOut();
disallowAnimate |= ((mLastWindowFlags ^ attrs.flags)
& FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
@@ -1363,7 +1362,7 @@
if (mStatusGuard == null) {
mStatusGuard = new View(mContext);
mStatusGuard.setBackgroundColor(mContext.getColor(
- R.color.input_method_navigation_guard));
+ R.color.decor_view_status_guard));
addView(mStatusGuard, indexOfChild(mStatusColorViewState.view),
new LayoutParams(LayoutParams.MATCH_PARENT,
mlp.topMargin, Gravity.START | Gravity.TOP));
@@ -1407,51 +1406,6 @@
return insets;
}
- private WindowInsets updateNavigationGuard(WindowInsets insets) {
- // IME windows lay out below the nav bar, but the content view must not (for back compat)
- // Only make this adjustment if the window is not requesting layout in overscan
- if (mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD
- && (mWindow.getAttributes().flags & FLAG_LAYOUT_IN_OVERSCAN) == 0) {
- // prevent the content view from including the nav bar height
- if (mWindow.mContentParent != null) {
- if (mWindow.mContentParent.getLayoutParams() instanceof MarginLayoutParams) {
- MarginLayoutParams mlp =
- (MarginLayoutParams) mWindow.mContentParent.getLayoutParams();
- mlp.bottomMargin = insets.getSystemWindowInsetBottom();
- mWindow.mContentParent.setLayoutParams(mlp);
- }
- }
- // position the navigation guard view, creating it if necessary
- if (mNavigationGuard == null) {
- mNavigationGuard = new View(mContext);
- mNavigationGuard.setBackgroundColor(mContext.getColor(
- R.color.input_method_navigation_guard));
- addView(mNavigationGuard, indexOfChild(mNavigationColorViewState.view),
- new LayoutParams(LayoutParams.MATCH_PARENT,
- insets.getSystemWindowInsetBottom(),
- Gravity.START | Gravity.BOTTOM));
- } else {
- LayoutParams lp = (LayoutParams) mNavigationGuard.getLayoutParams();
- lp.height = insets.getSystemWindowInsetBottom();
- mNavigationGuard.setLayoutParams(lp);
- }
- updateNavigationGuardColor();
- insets = insets.consumeSystemWindowInsets(
- false, false, false, true /* bottom */);
- }
- return insets;
- }
-
- void updateNavigationGuardColor() {
- if (mNavigationGuard != null) {
- // Make navigation bar guard invisible if the transparent color is specified.
- // Only TRANSPARENT is sufficient for hiding the navigation bar if the no software
- // keyboard is shown by IMS.
- mNavigationGuard.setVisibility(mWindow.getNavigationBarColor() == Color.TRANSPARENT ?
- View.INVISIBLE : View.VISIBLE);
- }
- }
-
/**
* Overrides the view outline when the activity enters picture-in-picture to ensure that it has
* an opaque shadow even if the window background is completely transparent. This only applies
@@ -2103,7 +2057,7 @@
for (int i = getChildCount() - 1; i >= 0; i--) {
View v = getChildAt(i);
if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view
- && v != mStatusGuard && v != mNavigationGuard) {
+ && v != mStatusGuard) {
removeViewAt(i);
}
}
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index 69184c3..e5d5685 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -35,7 +35,7 @@
void addStateMonitorCallback(IKeyguardStateCallback callback);
void verifyUnlock(IKeyguardExitCallback callback);
- void dismiss(IKeyguardDismissCallback callback);
+ void dismiss(IKeyguardDismissCallback callback, CharSequence message);
void onDreamingStarted();
void onDreamingStopped();
diff --git a/core/java/com/android/internal/policy/KeyguardDismissCallback.java b/core/java/com/android/internal/policy/KeyguardDismissCallback.java
new file mode 100644
index 0000000..38337ec
--- /dev/null
+++ b/core/java/com/android/internal/policy/KeyguardDismissCallback.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy;
+
+import android.os.RemoteException;
+import com.android.internal.policy.IKeyguardDismissCallback;
+
+/**
+ * @hide
+ */
+public class KeyguardDismissCallback extends IKeyguardDismissCallback.Stub {
+
+ @Override
+ public void onDismissError() throws RemoteException {
+ // To be overidden
+ }
+
+ @Override
+ public void onDismissSucceeded() throws RemoteException {
+ // To be overidden
+ }
+
+ @Override
+ public void onDismissCancelled() throws RemoteException {
+ // To be overidden
+ }
+}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index b13560c..e8ee29d 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -3807,7 +3807,6 @@
mForcedNavigationBarColor = true;
if (mDecor != null) {
mDecor.updateColorViews(null, false /* animate */);
- mDecor.updateNavigationGuardColor();
}
}
diff --git a/core/java/com/android/internal/print/DualDumpOutputStream.java b/core/java/com/android/internal/print/DualDumpOutputStream.java
new file mode 100644
index 0000000..4b10ef2
--- /dev/null
+++ b/core/java/com/android/internal/print/DualDumpOutputStream.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.print;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+
+/**
+ * Dump either to a proto or a print writer using the same interface.
+ *
+ * <p>This mirrors the interface of {@link ProtoOutputStream}.
+ */
+public class DualDumpOutputStream {
+ private static final String LOG_TAG = DualDumpOutputStream.class.getSimpleName();
+
+ // When writing to a proto, the proto
+ private final @Nullable ProtoOutputStream mProtoStream;
+
+ // When printing in clear text, the writer
+ private final @Nullable IndentingPrintWriter mIpw;
+ // Temporary storage of data when printing to mIpw
+ private final LinkedList<DumpObject> mDumpObjects = new LinkedList<>();
+
+ private static abstract class Dumpable {
+ final String name;
+
+ private Dumpable(String name) {
+ this.name = name;
+ }
+
+ abstract void print(IndentingPrintWriter ipw, boolean printName);
+ }
+
+ private static class DumpObject extends Dumpable {
+ private final LinkedHashMap<String, ArrayList<Dumpable>> mSubObjects = new LinkedHashMap<>();
+
+ private DumpObject(String name) {
+ super(name);
+ }
+
+ @Override
+ void print(IndentingPrintWriter ipw, boolean printName) {
+ if (printName) {
+ ipw.println(name + "={");
+ } else {
+ ipw.println("{");
+ }
+ ipw.increaseIndent();
+
+ for (ArrayList<Dumpable> subObject: mSubObjects.values()) {
+ int numDumpables = subObject.size();
+
+ if (numDumpables == 1) {
+ subObject.get(0).print(ipw, true);
+ } else {
+ ipw.println(subObject.get(0).name + "=[");
+ ipw.increaseIndent();
+
+ for (int i = 0; i < numDumpables; i++) {
+ subObject.get(i).print(ipw, false);
+ }
+
+ ipw.decreaseIndent();
+ ipw.println("]");
+ }
+ }
+
+ ipw.decreaseIndent();
+ ipw.println("}");
+ }
+
+ /**
+ * Add new field / subobject to this object.
+ *
+ * <p>If a name is added twice, they will be printed as a array
+ *
+ * @param fieldName name of the field added
+ * @param d The dumpable to add
+ */
+ public void add(String fieldName, Dumpable d) {
+ ArrayList<Dumpable> l = mSubObjects.get(fieldName);
+
+ if (l == null) {
+ l = new ArrayList<>(1);
+ mSubObjects.put(fieldName, l);
+ }
+
+ l.add(d);
+ }
+ }
+
+ private static class DumpField extends Dumpable {
+ private final String mValue;
+
+ private DumpField(String name, String value) {
+ super(name);
+ this.mValue = value;
+ }
+
+ @Override
+ void print(IndentingPrintWriter ipw, boolean printName) {
+ if (printName) {
+ ipw.println(name + "=" + mValue);
+ } else {
+ ipw.println(mValue);
+ }
+ }
+ }
+
+
+ /**
+ * Create a new DualDumpOutputStream. Only one output should be set.
+ *
+ * @param proto If dumping to proto the {@link ProtoOutputStream}
+ * @param ipw If dumping to a print writer, the {@link IndentingPrintWriter}
+ */
+ public DualDumpOutputStream(@Nullable ProtoOutputStream proto,
+ @Nullable IndentingPrintWriter ipw) {
+ if ((proto == null) == (ipw == null)) {
+ Log.e(LOG_TAG, "Cannot dump to clear text and proto at once. Ignoring proto");
+ proto = null;
+ }
+
+ mProtoStream = proto;
+ mIpw = ipw;
+
+ if (!isProto()) {
+ // Add root object
+ mDumpObjects.add(new DumpObject(null));
+ }
+ }
+
+ public void write(@NonNull String fieldName, long fieldId, double val) {
+ if (mProtoStream != null) {
+ mProtoStream.write(fieldId, val);
+ } else {
+ mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
+ }
+ }
+
+ public void write(@NonNull String fieldName, long fieldId, boolean val) {
+ if (mProtoStream != null) {
+ mProtoStream.write(fieldId, val);
+ } else {
+ mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
+ }
+ }
+
+ public void write(@NonNull String fieldName, long fieldId, int val) {
+ if (mProtoStream != null) {
+ mProtoStream.write(fieldId, val);
+ } else {
+ mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
+ }
+ }
+
+ public void write(@NonNull String fieldName, long fieldId, float val) {
+ if (mProtoStream != null) {
+ mProtoStream.write(fieldId, val);
+ } else {
+ mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
+ }
+ }
+
+ public void write(@NonNull String fieldName, long fieldId, byte[] val) {
+ if (mProtoStream != null) {
+ mProtoStream.write(fieldId, val);
+ } else {
+ mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, Arrays.toString(val)));
+ }
+ }
+
+ public void write(@NonNull String fieldName, long fieldId, long val) {
+ if (mProtoStream != null) {
+ mProtoStream.write(fieldId, val);
+ } else {
+ mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
+ }
+ }
+
+ public void write(@NonNull String fieldName, long fieldId, @Nullable String val) {
+ if (mProtoStream != null) {
+ mProtoStream.write(fieldId, val);
+ } else {
+ mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
+ }
+ }
+
+ public long start(@NonNull String fieldName, long fieldId) {
+ if (mProtoStream != null) {
+ return mProtoStream.start(fieldId);
+ } else {
+ DumpObject d = new DumpObject(fieldName);
+ mDumpObjects.getLast().add(fieldName, d);
+ mDumpObjects.addLast(d);
+ return System.identityHashCode(d);
+ }
+ }
+
+ public void end(long token) {
+ if (mProtoStream != null) {
+ mProtoStream.end(token);
+ } else {
+ if (System.identityHashCode(mDumpObjects.getLast()) != token) {
+ Log.w(LOG_TAG, "Unexpected token for ending " + mDumpObjects.getLast().name
+ + " at " + Arrays.toString(Thread.currentThread().getStackTrace()));
+ }
+ mDumpObjects.removeLast();
+ }
+ }
+
+ public void flush() {
+ if (mProtoStream != null) {
+ mProtoStream.flush();
+ } else {
+ if (mDumpObjects.size() == 1) {
+ mDumpObjects.getFirst().print(mIpw, false);
+
+ // Reset root object
+ mDumpObjects.clear();
+ mDumpObjects.add(new DumpObject(null));
+ }
+
+ mIpw.flush();
+ }
+ }
+
+ /**
+ * Add a dump from a different service into this dump.
+ *
+ * <p>Only for clear text dump. For proto dump use {@link #write(String, long, byte[])}.
+ *
+ * @param fieldName The name of the field
+ * @param nestedState The state of the dump
+ */
+ public void writeNested(@NonNull String fieldName, byte[] nestedState) {
+ if (mIpw == null) {
+ Log.w(LOG_TAG, "writeNested does not work for proto logging");
+ return;
+ }
+
+ mDumpObjects.getLast().add(fieldName,
+ new DumpField(fieldName, (new String(nestedState, StandardCharsets.UTF_8)).trim()));
+ }
+
+ /**
+ * @return {@code true} iff we are dumping to a proto
+ */
+ public boolean isProto() {
+ return mProtoStream != null;
+ }
+}
diff --git a/core/java/com/android/internal/print/DumpUtils.java b/core/java/com/android/internal/print/DumpUtils.java
index 28c7fc2..3192d5c 100644
--- a/core/java/com/android/internal/print/DumpUtils.java
+++ b/core/java/com/android/internal/print/DumpUtils.java
@@ -39,7 +39,6 @@
import android.service.print.PrinterIdProto;
import android.service.print.PrinterInfoProto;
import android.service.print.ResolutionProto;
-import android.util.proto.ProtoOutputStream;
/**
* Utilities for dumping print related proto buffer
@@ -49,13 +48,14 @@
* Write a string to a proto if the string is not {@code null}.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the string
* @param string The string to write
*/
- public static void writeStringIfNotNull(@NonNull ProtoOutputStream proto, long id,
- @Nullable String string) {
+ public static void writeStringIfNotNull(@NonNull DualDumpOutputStream proto, String idName,
+ long id, @Nullable String string) {
if (string != null) {
- proto.write(id, string);
+ proto.write(idName, id, string);
}
}
@@ -63,14 +63,15 @@
* Write a {@link ComponentName} to a proto.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param component The component name to write
*/
- public static void writeComponentName(@NonNull ProtoOutputStream proto, long id,
- @NonNull ComponentName component) {
- long token = proto.start(id);
- proto.write(ComponentNameProto.PACKAGE_NAME, component.getPackageName());
- proto.write(ComponentNameProto.CLASS_NAME, component.getClassName());
+ public static void writeComponentName(@NonNull DualDumpOutputStream proto, String idName,
+ long id, @NonNull ComponentName component) {
+ long token = proto.start(idName, id);
+ proto.write("package_name", ComponentNameProto.PACKAGE_NAME, component.getPackageName());
+ proto.write("class_name", ComponentNameProto.CLASS_NAME, component.getClassName());
proto.end(token);
}
@@ -78,14 +79,16 @@
* Write a {@link PrinterId} to a proto.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param printerId The printer id to write
*/
- public static void writePrinterId(@NonNull ProtoOutputStream proto, long id,
+ public static void writePrinterId(@NonNull DualDumpOutputStream proto, String idName, long id,
@NonNull PrinterId printerId) {
- long token = proto.start(id);
- writeComponentName(proto, PrinterIdProto.SERVICE_NAME, printerId.getServiceName());
- proto.write(PrinterIdProto.LOCAL_ID, printerId.getLocalId());
+ long token = proto.start(idName, id);
+ writeComponentName(proto, "service_name", PrinterIdProto.SERVICE_NAME,
+ printerId.getServiceName());
+ proto.write("local_id", PrinterIdProto.LOCAL_ID, printerId.getLocalId());
proto.end(token);
}
@@ -93,71 +96,76 @@
* Write a {@link PrinterCapabilitiesInfo} to a proto.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param cap The capabilities to write
*/
public static void writePrinterCapabilities(@NonNull Context context,
- @NonNull ProtoOutputStream proto, long id, @NonNull PrinterCapabilitiesInfo cap) {
- long token = proto.start(id);
- writeMargins(proto, PrinterCapabilitiesProto.MIN_MARGINS, cap.getMinMargins());
+ @NonNull DualDumpOutputStream proto, String idName, long id,
+ @NonNull PrinterCapabilitiesInfo cap) {
+ long token = proto.start(idName, id);
+ writeMargins(proto, "min_margins", PrinterCapabilitiesProto.MIN_MARGINS,
+ cap.getMinMargins());
int numMediaSizes = cap.getMediaSizes().size();
for (int i = 0; i < numMediaSizes; i++) {
- writeMediaSize(context, proto, PrinterCapabilitiesProto.MEDIA_SIZES,
+ writeMediaSize(context, proto, "media_sizes", PrinterCapabilitiesProto.MEDIA_SIZES,
cap.getMediaSizes().get(i));
}
int numResolutions = cap.getResolutions().size();
for (int i = 0; i < numResolutions; i++) {
- writeResolution(proto, PrinterCapabilitiesProto.RESOLUTIONS,
+ writeResolution(proto, "resolutions", PrinterCapabilitiesProto.RESOLUTIONS,
cap.getResolutions().get(i));
}
if ((cap.getColorModes() & PrintAttributes.COLOR_MODE_MONOCHROME) != 0) {
- proto.write(PrinterCapabilitiesProto.COLOR_MODES,
+ proto.write("color_modes", PrinterCapabilitiesProto.COLOR_MODES,
PrintAttributesProto.COLOR_MODE_MONOCHROME);
}
if ((cap.getColorModes() & PrintAttributes.COLOR_MODE_COLOR) != 0) {
- proto.write(PrinterCapabilitiesProto.COLOR_MODES,
+ proto.write("color_modes", PrinterCapabilitiesProto.COLOR_MODES,
PrintAttributesProto.COLOR_MODE_COLOR);
}
if ((cap.getDuplexModes() & PrintAttributes.DUPLEX_MODE_NONE) != 0) {
- proto.write(PrinterCapabilitiesProto.DUPLEX_MODES,
+ proto.write("duplex_modes", PrinterCapabilitiesProto.DUPLEX_MODES,
PrintAttributesProto.DUPLEX_MODE_NONE);
}
if ((cap.getDuplexModes() & PrintAttributes.DUPLEX_MODE_LONG_EDGE) != 0) {
- proto.write(PrinterCapabilitiesProto.DUPLEX_MODES,
+ proto.write("duplex_modes", PrinterCapabilitiesProto.DUPLEX_MODES,
PrintAttributesProto.DUPLEX_MODE_LONG_EDGE);
}
if ((cap.getDuplexModes() & PrintAttributes.DUPLEX_MODE_SHORT_EDGE) != 0) {
- proto.write(PrinterCapabilitiesProto.DUPLEX_MODES,
+ proto.write("duplex_modes", PrinterCapabilitiesProto.DUPLEX_MODES,
PrintAttributesProto.DUPLEX_MODE_SHORT_EDGE);
}
proto.end(token);
}
-
/**
* Write a {@link PrinterInfo} to a proto.
*
* @param context The context used to resolve resources
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param info The printer info to write
*/
- public static void writePrinterInfo(@NonNull Context context, @NonNull ProtoOutputStream proto,
- long id, @NonNull PrinterInfo info) {
- long token = proto.start(id);
- writePrinterId(proto, PrinterInfoProto.ID, info.getId());
- proto.write(PrinterInfoProto.NAME, info.getName());
- proto.write(PrinterInfoProto.STATUS, info.getStatus());
- proto.write(PrinterInfoProto.DESCRIPTION, info.getDescription());
+ public static void writePrinterInfo(@NonNull Context context,
+ @NonNull DualDumpOutputStream proto, String idName, long id,
+ @NonNull PrinterInfo info) {
+ long token = proto.start(idName, id);
+ writePrinterId(proto, "id", PrinterInfoProto.ID, info.getId());
+ proto.write("name", PrinterInfoProto.NAME, info.getName());
+ proto.write("status", PrinterInfoProto.STATUS, info.getStatus());
+ proto.write("description", PrinterInfoProto.DESCRIPTION, info.getDescription());
PrinterCapabilitiesInfo cap = info.getCapabilities();
if (cap != null) {
- writePrinterCapabilities(context, proto, PrinterInfoProto.CAPABILITIES, cap);
+ writePrinterCapabilities(context, proto, "capabilities", PrinterInfoProto.CAPABILITIES,
+ cap);
}
proto.end(token);
@@ -168,16 +176,17 @@
*
* @param context The context used to resolve resources
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param mediaSize The media size to write
*/
- public static void writeMediaSize(@NonNull Context context, @NonNull ProtoOutputStream proto,
- long id, @NonNull PrintAttributes.MediaSize mediaSize) {
- long token = proto.start(id);
- proto.write(MediaSizeProto.ID, mediaSize.getId());
- proto.write(MediaSizeProto.LABEL, mediaSize.getLabel(context.getPackageManager()));
- proto.write(MediaSizeProto.HEIGHT_MILS, mediaSize.getHeightMils());
- proto.write(MediaSizeProto.WIDTH_MILS, mediaSize.getWidthMils());
+ public static void writeMediaSize(@NonNull Context context, @NonNull DualDumpOutputStream proto,
+ String idName, long id, @NonNull PrintAttributes.MediaSize mediaSize) {
+ long token = proto.start(idName, id);
+ proto.write("id", MediaSizeProto.ID, mediaSize.getId());
+ proto.write("label", MediaSizeProto.LABEL, mediaSize.getLabel(context.getPackageManager()));
+ proto.write("height_mils", MediaSizeProto.HEIGHT_MILS, mediaSize.getHeightMils());
+ proto.write("width_mils", MediaSizeProto.WIDTH_MILS, mediaSize.getWidthMils());
proto.end(token);
}
@@ -185,16 +194,17 @@
* Write a {@link PrintAttributes.Resolution} to a proto.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param res The resolution to write
*/
- public static void writeResolution(@NonNull ProtoOutputStream proto, long id,
+ public static void writeResolution(@NonNull DualDumpOutputStream proto, String idName, long id,
@NonNull PrintAttributes.Resolution res) {
- long token = proto.start(id);
- proto.write(ResolutionProto.ID, res.getId());
- proto.write(ResolutionProto.LABEL, res.getLabel());
- proto.write(ResolutionProto.HORIZONTAL_DPI, res.getHorizontalDpi());
- proto.write(ResolutionProto.VERTICAL_DPI, res.getVerticalDpi());
+ long token = proto.start(idName, id);
+ proto.write("id", ResolutionProto.ID, res.getId());
+ proto.write("label", ResolutionProto.LABEL, res.getLabel());
+ proto.write("horizontal_DPI", ResolutionProto.HORIZONTAL_DPI, res.getHorizontalDpi());
+ proto.write("veritical_DPI", ResolutionProto.VERTICAL_DPI, res.getVerticalDpi());
proto.end(token);
}
@@ -202,16 +212,17 @@
* Write a {@link PrintAttributes.Margins} to a proto.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param margins The margins to write
*/
- public static void writeMargins(@NonNull ProtoOutputStream proto, long id,
+ public static void writeMargins(@NonNull DualDumpOutputStream proto, String idName, long id,
@NonNull PrintAttributes.Margins margins) {
- long token = proto.start(id);
- proto.write(MarginsProto.TOP_MILS, margins.getTopMils());
- proto.write(MarginsProto.LEFT_MILS, margins.getLeftMils());
- proto.write(MarginsProto.RIGHT_MILS, margins.getRightMils());
- proto.write(MarginsProto.BOTTOM_MILS, margins.getBottomMils());
+ long token = proto.start(idName, id);
+ proto.write("top_mils", MarginsProto.TOP_MILS, margins.getTopMils());
+ proto.write("left_mils", MarginsProto.LEFT_MILS, margins.getLeftMils());
+ proto.write("right_mils", MarginsProto.RIGHT_MILS, margins.getRightMils());
+ proto.write("bottom_mils", MarginsProto.BOTTOM_MILS, margins.getBottomMils());
proto.end(token);
}
@@ -220,32 +231,34 @@
*
* @param context The context used to resolve resources
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param attributes The attributes to write
*/
public static void writePrintAttributes(@NonNull Context context,
- @NonNull ProtoOutputStream proto, long id, @NonNull PrintAttributes attributes) {
- long token = proto.start(id);
+ @NonNull DualDumpOutputStream proto, String idName, long id,
+ @NonNull PrintAttributes attributes) {
+ long token = proto.start(idName, id);
PrintAttributes.MediaSize mediaSize = attributes.getMediaSize();
if (mediaSize != null) {
- writeMediaSize(context, proto, PrintAttributesProto.MEDIA_SIZE, mediaSize);
+ writeMediaSize(context, proto, "media_size", PrintAttributesProto.MEDIA_SIZE, mediaSize);
}
- proto.write(PrintAttributesProto.IS_PORTRAIT, attributes.isPortrait());
+ proto.write("is_portrait", PrintAttributesProto.IS_PORTRAIT, attributes.isPortrait());
PrintAttributes.Resolution res = attributes.getResolution();
if (res != null) {
- writeResolution(proto, PrintAttributesProto.RESOLUTION, res);
+ writeResolution(proto, "resolution", PrintAttributesProto.RESOLUTION, res);
}
PrintAttributes.Margins minMargins = attributes.getMinMargins();
if (minMargins != null) {
- writeMargins(proto, PrintAttributesProto.MIN_MARGINS, minMargins);
+ writeMargins(proto, "min_margings", PrintAttributesProto.MIN_MARGINS, minMargins);
}
- proto.write(PrintAttributesProto.COLOR_MODE, attributes.getColorMode());
- proto.write(PrintAttributesProto.DUPLEX_MODE, attributes.getDuplexMode());
+ proto.write("color_mode", PrintAttributesProto.COLOR_MODE, attributes.getColorMode());
+ proto.write("duplex_mode", PrintAttributesProto.DUPLEX_MODE, attributes.getDuplexMode());
proto.end(token);
}
@@ -253,21 +266,22 @@
* Write a {@link PrintDocumentInfo} to a proto.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param info The info to write
*/
- public static void writePrintDocumentInfo(@NonNull ProtoOutputStream proto, long id,
- @NonNull PrintDocumentInfo info) {
- long token = proto.start(id);
- proto.write(PrintDocumentInfoProto.NAME, info.getName());
+ public static void writePrintDocumentInfo(@NonNull DualDumpOutputStream proto, String idName,
+ long id, @NonNull PrintDocumentInfo info) {
+ long token = proto.start(idName, id);
+ proto.write("name", PrintDocumentInfoProto.NAME, info.getName());
int pageCount = info.getPageCount();
if (pageCount != PrintDocumentInfo.PAGE_COUNT_UNKNOWN) {
- proto.write(PrintDocumentInfoProto.PAGE_COUNT, pageCount);
+ proto.write("page_count", PrintDocumentInfoProto.PAGE_COUNT, pageCount);
}
- proto.write(PrintDocumentInfoProto.CONTENT_TYPE, info.getContentType());
- proto.write(PrintDocumentInfoProto.DATA_SIZE, info.getDataSize());
+ proto.write("content_type", PrintDocumentInfoProto.CONTENT_TYPE, info.getContentType());
+ proto.write("data_size", PrintDocumentInfoProto.DATA_SIZE, info.getDataSize());
proto.end(token);
}
@@ -275,14 +289,15 @@
* Write a {@link PageRange} to a proto.
*
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param range The range to write
*/
- public static void writePageRange(@NonNull ProtoOutputStream proto, long id,
+ public static void writePageRange(@NonNull DualDumpOutputStream proto, String idName, long id,
@NonNull PageRange range) {
- long token = proto.start(id);
- proto.write(PageRangeProto.START, range.getStart());
- proto.write(PageRangeProto.END, range.getEnd());
+ long token = proto.start(idName, id);
+ proto.write("start", PageRangeProto.START, range.getStart());
+ proto.write("end", PageRangeProto.END, range.getEnd());
proto.end(token);
}
@@ -291,64 +306,70 @@
*
* @param context The context used to resolve resources
* @param proto The proto to write to
+ * @param idName Clear text name of the proto-id
* @param id The proto-id of the component name
* @param printJobInfo The print job info to write
*/
- public static void writePrintJobInfo(@NonNull Context context, @NonNull ProtoOutputStream proto,
- long id, @NonNull PrintJobInfo printJobInfo) {
- long token = proto.start(id);
- proto.write(PrintJobInfoProto.LABEL, printJobInfo.getLabel());
+ public static void writePrintJobInfo(@NonNull Context context,
+ @NonNull DualDumpOutputStream proto, String idName, long id,
+ @NonNull PrintJobInfo printJobInfo) {
+ long token = proto.start(idName, id);
+ proto.write("label", PrintJobInfoProto.LABEL, printJobInfo.getLabel());
PrintJobId printJobId = printJobInfo.getId();
if (printJobId != null) {
- proto.write(PrintJobInfoProto.PRINT_JOB_ID, printJobId.flattenToString());
+ proto.write("print_job_id", PrintJobInfoProto.PRINT_JOB_ID,
+ printJobId.flattenToString());
}
int state = printJobInfo.getState();
if (state >= PrintJobInfoProto.STATE_CREATED && state <= PrintJobInfoProto.STATE_CANCELED) {
- proto.write(PrintJobInfoProto.STATE, state);
+ proto.write("state", PrintJobInfoProto.STATE, state);
} else {
- proto.write(PrintJobInfoProto.STATE, PrintJobInfoProto.STATE_UNKNOWN);
+ proto.write("state", PrintJobInfoProto.STATE, PrintJobInfoProto.STATE_UNKNOWN);
}
PrinterId printer = printJobInfo.getPrinterId();
if (printer != null) {
- writePrinterId(proto, PrintJobInfoProto.PRINTER, printer);
+ writePrinterId(proto, "printer", PrintJobInfoProto.PRINTER, printer);
}
String tag = printJobInfo.getTag();
if (tag != null) {
- proto.write(PrintJobInfoProto.TAG, tag);
+ proto.write("tag", PrintJobInfoProto.TAG, tag);
}
- proto.write(PrintJobInfoProto.CREATION_TIME, printJobInfo.getCreationTime());
+ proto.write("creation_time", PrintJobInfoProto.CREATION_TIME,
+ printJobInfo.getCreationTime());
PrintAttributes attributes = printJobInfo.getAttributes();
if (attributes != null) {
- writePrintAttributes(context, proto, PrintJobInfoProto.ATTRIBUTES, attributes);
+ writePrintAttributes(context, proto, "attributes", PrintJobInfoProto.ATTRIBUTES,
+ attributes);
}
PrintDocumentInfo docInfo = printJobInfo.getDocumentInfo();
if (docInfo != null) {
- writePrintDocumentInfo(proto, PrintJobInfoProto.DOCUMENT_INFO, docInfo);
+ writePrintDocumentInfo(proto, "document_info", PrintJobInfoProto.DOCUMENT_INFO,
+ docInfo);
}
- proto.write(PrintJobInfoProto.IS_CANCELING, printJobInfo.isCancelling());
+ proto.write("is_canceling", PrintJobInfoProto.IS_CANCELING, printJobInfo.isCancelling());
PageRange[] pages = printJobInfo.getPages();
if (pages != null) {
for (int i = 0; i < pages.length; i++) {
- writePageRange(proto, PrintJobInfoProto.PAGES, pages[i]);
+ writePageRange(proto, "pages", PrintJobInfoProto.PAGES, pages[i]);
}
}
- proto.write(PrintJobInfoProto.HAS_ADVANCED_OPTIONS,
+ proto.write("has_advanced_options", PrintJobInfoProto.HAS_ADVANCED_OPTIONS,
printJobInfo.getAdvancedOptions() != null);
- proto.write(PrintJobInfoProto.PROGRESS, printJobInfo.getProgress());
+ proto.write("progress", PrintJobInfoProto.PROGRESS, printJobInfo.getProgress());
CharSequence status = printJobInfo.getStatus(context.getPackageManager());
if (status != null) {
- proto.write(PrintJobInfoProto.STATUS, status.toString());
+ proto.write("status", PrintJobInfoProto.STATUS, status.toString());
}
proto.end(token);
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index aa85668..7b023f4 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -184,6 +184,13 @@
}
/**
+ * Length of the given collection or 0 if it's null.
+ */
+ public static int size(@Nullable Collection<?> collection) {
+ return collection == null ? 0 : collection.size();
+ }
+
+ /**
* Checks that value is present as at least one of the elements of the array.
* @param array the array to check in
* @param value the value to check for
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 7985e57..2f2c747 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -16,8 +16,6 @@
package com.android.internal.util;
-import static com.android.internal.util.ArrayUtils.isEmpty;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.ArraySet;
@@ -173,13 +171,20 @@
}
/**
- * Returns the size of the given list, or 0 if the list is null
+ * Returns the size of the given collection, or 0 if null
*/
public static int size(@Nullable Collection<?> cur) {
return cur != null ? cur.size() : 0;
}
/**
+ * Returns whether the given collection {@link Collection#isEmpty is empty} or {@code null}
+ */
+ public static boolean isEmpty(@Nullable Collection<?> cur) {
+ return size(cur) == 0;
+ }
+
+ /**
* Returns the elements of the given list that are of type {@code c}
*/
public static @NonNull <T> List<T> filter(@Nullable List<?> list, Class<T> c) {
diff --git a/core/java/com/android/internal/util/ObjectUtils.java b/core/java/com/android/internal/util/ObjectUtils.java
index 59e5a64..379602a 100644
--- a/core/java/com/android/internal/util/ObjectUtils.java
+++ b/core/java/com/android/internal/util/ObjectUtils.java
@@ -29,6 +29,9 @@
return a != null ? a : Preconditions.checkNotNull(b);
}
+ /**
+ * Compares two {@link Nullable} objects with {@code null} values considered the smallest
+ */
public static <T extends Comparable> int compare(@Nullable T a, @Nullable T b) {
if (a != null) {
return (b != null) ? a.compareTo(b) : 1;
@@ -36,4 +39,13 @@
return (b != null) ? -1 : 0;
}
}
+
+ /**
+ * @return {@code null} if the given instance is not of the given calss, or the given
+ * instance otherwise
+ */
+ @Nullable
+ public static <S, T extends S> T castOrNull(@Nullable S instance, @NonNull Class<T> c) {
+ return c.isInstance(instance) ? (T) instance : null;
+ }
}
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
new file mode 100644
index 0000000..f7ea787
--- /dev/null
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -0,0 +1,128 @@
+package com.android.internal.util;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+public class ScreenshotHelper {
+ private static final String TAG = "ScreenshotHelper";
+
+ private static final String SYSUI_PACKAGE = "com.android.systemui";
+ private static final String SYSUI_SCREENSHOT_SERVICE =
+ "com.android.systemui.screenshot.TakeScreenshotService";
+ private static final String SYSUI_SCREENSHOT_ERROR_RECEIVER =
+ "com.android.systemui.screenshot.ScreenshotServiceErrorReceiver";
+
+ // Time until we give up on the screenshot & show an error instead.
+ private final int SCREENSHOT_TIMEOUT_MS = 10000;
+
+ private final Object mScreenshotLock = new Object();
+ private ServiceConnection mScreenshotConnection = null;
+ private final Context mContext;
+
+ public ScreenshotHelper(Context context) {
+ mContext = context;
+ }
+
+ public void takeScreenshot(final int screenshotType, final boolean hasStatus,
+ final boolean hasNav, Handler handler) {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != null) {
+ return;
+ }
+ final ComponentName serviceComponent = new ComponentName(SYSUI_PACKAGE,
+ SYSUI_SCREENSHOT_SERVICE);
+ final Intent serviceIntent = new Intent();
+
+ final Runnable mScreenshotTimeout = new Runnable() {
+ @Override public void run() {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != null) {
+ mContext.unbindService(mScreenshotConnection);
+ mScreenshotConnection = null;
+ notifyScreenshotError();
+ }
+ }
+ }
+ };
+
+ serviceIntent.setComponent(serviceComponent);
+ ServiceConnection conn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != this) {
+ return;
+ }
+ Messenger messenger = new Messenger(service);
+ Message msg = Message.obtain(null, screenshotType);
+ final ServiceConnection myConn = this;
+ Handler h = new Handler(handler.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection == myConn) {
+ mContext.unbindService(mScreenshotConnection);
+ mScreenshotConnection = null;
+ handler.removeCallbacks(mScreenshotTimeout);
+ }
+ }
+ }
+ };
+ msg.replyTo = new Messenger(h);
+ msg.arg1 = hasStatus ? 1: 0;
+ msg.arg2 = hasNav ? 1: 0;
+ try {
+ messenger.send(msg);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't take screenshot: " + e);
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != null) {
+ mContext.unbindService(mScreenshotConnection);
+ mScreenshotConnection = null;
+ handler.removeCallbacks(mScreenshotTimeout);
+ notifyScreenshotError();
+ }
+ }
+ }
+ };
+ if (mContext.bindServiceAsUser(serviceIntent, conn,
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
+ UserHandle.CURRENT)) {
+ mScreenshotConnection = conn;
+ handler.postDelayed(mScreenshotTimeout, SCREENSHOT_TIMEOUT_MS);
+ }
+ }
+ }
+
+ /**
+ * Notifies the screenshot service to show an error.
+ */
+ private void notifyScreenshotError() {
+ // If the service process is killed, then ask it to clean up after itself
+ final ComponentName errorComponent = new ComponentName(SYSUI_PACKAGE,
+ SYSUI_SCREENSHOT_ERROR_RECEIVER);
+ // Broadcast needs to have a valid action. We'll just pick
+ // a generic one, since the receiver here doesn't care.
+ Intent errorIntent = new Intent(Intent.ACTION_USER_PRESENT);
+ errorIntent.setComponent(errorComponent);
+ errorIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
+ Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.sendBroadcastAsUser(errorIntent, UserHandle.CURRENT);
+ }
+
+}
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 4e7df28..6ed8973 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -19,9 +19,9 @@
import android.app.PendingIntent;
import android.app.trust.IStrongAuthTracker;
import android.os.Bundle;
-import android.security.recoverablekeystore.KeyEntryRecoveryData;
-import android.security.recoverablekeystore.KeyStoreRecoveryData;
-import android.security.recoverablekeystore.KeyStoreRecoveryMetadata;
+import android.security.keystore.WrappedApplicationKey;
+import android.security.keystore.KeychainSnapshot;
+import android.security.keystore.KeychainProtectionParams;
import com.android.internal.widget.ICheckCredentialProgressCallback;
import com.android.internal.widget.VerifyCredentialResponse;
@@ -60,25 +60,25 @@
in byte[] token, int requestedQuality, int userId);
void unlockUserWithToken(long tokenHandle, in byte[] token, int userId);
- // RecoverableKeyStoreLoader methods.
+ // Keystore RecoveryManager methods.
// {@code ServiceSpecificException} may be thrown to signal an error, which caller can
- // convert to {@code RecoverableKeyStoreLoader}.
+ // convert to {@code RecoveryManagerException}.
void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList);
- KeyStoreRecoveryData getRecoveryData(in byte[] account);
+ KeychainSnapshot getRecoveryData(in byte[] account);
byte[] generateAndStoreKey(String alias);
void removeKey(String alias);
void setSnapshotCreatedPendingIntent(in PendingIntent intent);
Map getRecoverySnapshotVersions();
- void setServerParameters(long serverParameters);
+ void setServerParams(in byte[] serverParams);
void setRecoveryStatus(in String packageName, in String[] aliases, int status);
Map getRecoveryStatus(in String packageName);
void setRecoverySecretTypes(in int[] secretTypes);
int[] getRecoverySecretTypes();
int[] getPendingRecoverySecretTypes();
- void recoverySecretAvailable(in KeyStoreRecoveryMetadata recoverySecret);
+ void recoverySecretAvailable(in KeychainProtectionParams recoverySecret);
byte[] startRecoverySession(in String sessionId,
in byte[] verifierPublicKey, in byte[] vaultParams, in byte[] vaultChallenge,
- in List<KeyStoreRecoveryMetadata> secrets);
+ in List<KeychainProtectionParams> secrets);
Map/*<String, byte[]>*/ recoverKeys(in String sessionId, in byte[] recoveryKeyBlob,
- in List<KeyEntryRecoveryData> applicationKeys);
+ in List<WrappedApplicationKey> applicationKeys);
}
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index 792f921..33977f3 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -20,17 +20,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StyleRes;
+import android.app.Notification;
import android.content.Context;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Pools;
-import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -41,7 +36,6 @@
import android.widget.RemoteViews;
import com.android.internal.R;
-import com.android.internal.util.NotificationColorUtil;
import java.util.ArrayList;
import java.util.List;
@@ -60,12 +54,13 @@
private int mLayoutColor;
private CharSequence mAvatarName = "";
private Icon mAvatarIcon;
- private ColorFilter mMessageBackgroundFilter;
private int mTextColor;
private List<MessagingMessage> mMessages;
private ArrayList<MessagingMessage> mAddedMessages = new ArrayList<>();
private boolean mFirstLayout;
private boolean mIsHidingAnimated;
+ private boolean mNeedsGeneratedAvatar;
+ private Notification.Person mSender;
public MessagingGroup(@NonNull Context context) {
super(context);
@@ -94,27 +89,20 @@
mAvatarView = findViewById(R.id.message_icon);
}
- public void setSender(CharSequence sender) {
- if (sender == null) {
- mAvatarView.setVisibility(GONE);
- mSenderName.setVisibility(GONE);
- setGravity(Gravity.END);
- mMessageBackgroundFilter = new PorterDuffColorFilter(mLayoutColor,
- PorterDuff.Mode.SRC_ATOP);
- mTextColor = NotificationColorUtil.isColorLight(mLayoutColor) ? getNormalTextColor()
- : Color.WHITE;
- } else {
- mSenderName.setText(sender);
- mAvatarView.setVisibility(VISIBLE);
- mSenderName.setVisibility(VISIBLE);
- setGravity(Gravity.START);
- mMessageBackgroundFilter = null;
- mTextColor = getNormalTextColor();
+ public void setSender(Notification.Person sender) {
+ mSender = sender;
+ mSenderName.setText(sender.getName());
+ mNeedsGeneratedAvatar = sender.getIcon() == null;
+ if (!mNeedsGeneratedAvatar) {
+ setAvatar(sender.getIcon());
}
+ mAvatarView.setVisibility(VISIBLE);
+ mSenderName.setVisibility(VISIBLE);
+ mTextColor = getNormalTextColor();
}
private int getNormalTextColor() {
- return mContext.getColor(R.color.notification_primary_text_color_light);
+ return mContext.getColor(R.color.notification_secondary_text_color_light);
}
public void setAvatar(Icon icon) {
@@ -207,10 +195,6 @@
return mSenderName.getText();
}
- public void setSenderVisible(boolean visible) {
- mSenderName.setVisibility(visible ? VISIBLE : GONE);
- }
-
public static void dropCache() {
sInstancePool = new Pools.SynchronizedPool<>(10);
}
@@ -317,12 +301,6 @@
mMessageContainer.removeView(message);
mMessageContainer.addView(message, messageIndex);
}
- // Let's make sure the message color is correct
- Drawable targetDrawable = message.getBackground();
-
- if (targetDrawable != null) {
- targetDrawable.mutate().setColorFilter(mMessageBackgroundFilter);
- }
message.setTextColor(mTextColor);
}
mMessages = group;
@@ -379,7 +357,7 @@
return 0;
}
- public View getSender() {
+ public View getSenderView() {
return mSenderName;
}
@@ -390,4 +368,12 @@
public MessagingLinearLayout getMessageContainer() {
return mMessageContainer;
}
+
+ public boolean needsGeneratedAvatar() {
+ return mNeedsGeneratedAvatar;
+ }
+
+ public Notification.Person getSender() {
+ return mSender;
+ }
}
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 2acdc01..7a64cad 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -35,7 +35,6 @@
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.RemotableViewMethod;
-import android.view.View;
import android.view.ViewTreeObserver;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
@@ -69,7 +68,6 @@
private List<MessagingMessage> mMessages = new ArrayList<>();
private List<MessagingMessage> mHistoricMessages = new ArrayList<>();
private MessagingLinearLayout mMessagingLinearLayout;
- private View mContractedMessage;
private boolean mShowHistoricMessages;
private ArrayList<MessagingGroup> mGroups = new ArrayList<>();
private TextView mTitleView;
@@ -81,6 +79,7 @@
private Icon mLargeIcon;
private boolean mIsOneToOne;
private ArrayList<MessagingGroup> mAddedGroups = new ArrayList<>();
+ private Notification.Person mUser;
public MessagingLayout(@NonNull Context context) {
super(context);
@@ -129,14 +128,30 @@
Parcelable[] histMessages = extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES);
List<Notification.MessagingStyle.Message> newHistoricMessages
= Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages);
+ setUser(extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON));
mConversationTitle = null;
TextView headerText = findViewById(R.id.header_text);
if (headerText != null) {
mConversationTitle = headerText.getText();
}
+ addRemoteInputHistoryToMessages(newMessages,
+ extras.getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY));
bind(newMessages, newHistoricMessages);
}
+ private void addRemoteInputHistoryToMessages(
+ List<Notification.MessagingStyle.Message> newMessages,
+ CharSequence[] remoteInputHistory) {
+ if (remoteInputHistory == null || remoteInputHistory.length == 0) {
+ return;
+ }
+ for (int i = remoteInputHistory.length - 1; i >= 0; i--) {
+ CharSequence message = remoteInputHistory[i];
+ newMessages.add(new Notification.MessagingStyle.Message(
+ message, 0, (Notification.Person) null));
+ }
+ }
+
private void bind(List<Notification.MessagingStyle.Message> newMessages,
List<Notification.MessagingStyle.Message> newHistoricMessages) {
@@ -152,7 +167,6 @@
mMessages = messages;
mHistoricMessages = historicMessages;
- updateContractedMessage();
updateHistoricMessageVisibility();
updateTitleAndNamesDisplay();
}
@@ -163,12 +177,10 @@
for (int i = 0; i < mGroups.size(); i++) {
MessagingGroup group = mGroups.get(i);
CharSequence senderName = group.getSenderName();
- if (TextUtils.isEmpty(senderName)) {
+ if (!group.needsGeneratedAvatar() || TextUtils.isEmpty(senderName)) {
continue;
}
- boolean visible = !mIsOneToOne;
- group.setSenderVisible(visible);
- if ((visible || mLargeIcon == null) && !uniqueNames.containsKey(senderName)) {
+ if (!uniqueNames.containsKey(senderName)) {
char c = senderName.charAt(0);
if (uniqueCharacters.containsKey(c)) {
// this character was already used, lets make it more unique. We first need to
@@ -191,8 +203,10 @@
for (int i = 0; i < mGroups.size(); i++) {
// Let's now set the avatars
MessagingGroup group = mGroups.get(i);
+ boolean isOwnMessage = group.getSender() == mUser;
CharSequence senderName = group.getSenderName();
- if (TextUtils.isEmpty(senderName) || (mIsOneToOne && mLargeIcon != null)) {
+ if (!group.needsGeneratedAvatar() || TextUtils.isEmpty(senderName)
+ || (mIsOneToOne && mLargeIcon != null && !isOwnMessage)) {
continue;
}
String symbol = uniqueNames.get(senderName);
@@ -207,10 +221,10 @@
// Let's now set the avatars
MessagingGroup group = mGroups.get(i);
CharSequence senderName = group.getSenderName();
- if (TextUtils.isEmpty(senderName)) {
+ if (!group.needsGeneratedAvatar() || TextUtils.isEmpty(senderName)) {
continue;
}
- if (mIsOneToOne && mLargeIcon != null) {
+ if (mIsOneToOne && mLargeIcon != null && group.getSender() != mUser) {
group.setAvatar(mLargeIcon);
} else {
Icon cachedIcon = cachedAvatars.get(senderName);
@@ -234,7 +248,7 @@
canvas.drawCircle(radius, radius, radius, mPaint);
boolean needDarkText = ColorUtils.calculateLuminance(color) > 0.5f;
mTextPaint.setColor(needDarkText ? Color.BLACK : Color.WHITE);
- mTextPaint.setTextSize(symbol.length() == 1 ? mAvatarSize * 0.75f : mAvatarSize * 0.4f);
+ mTextPaint.setTextSize(symbol.length() == 1 ? mAvatarSize * 0.5f : mAvatarSize * 0.3f);
int yPos = (int) (radius - ((mTextPaint.descent() + mTextPaint.ascent()) / 2)) ;
canvas.drawText(symbol, radius, yPos, mTextPaint);
return Icon.createWithBitmap(bitmap);
@@ -270,11 +284,21 @@
mIsOneToOne = oneToOne;
}
+ public void setUser(Notification.Person user) {
+ mUser = user;
+ if (mUser.getIcon() == null) {
+ Icon userIcon = Icon.createWithResource(getContext(),
+ com.android.internal.R.drawable.messaging_user);
+ userIcon.setTint(mLayoutColor);
+ mUser.setIcon(userIcon);
+ }
+ }
+
private void addMessagesToGroups(List<MessagingMessage> historicMessages,
List<MessagingMessage> messages) {
// Let's first find our groups!
List<List<MessagingMessage>> groups = new ArrayList<>();
- List<CharSequence> senders = new ArrayList<>();
+ List<Notification.Person> senders = new ArrayList<>();
// Lets first find the groups
findGroups(historicMessages, messages, groups, senders);
@@ -283,7 +307,8 @@
createGroupViews(groups, senders);
}
- private void createGroupViews(List<List<MessagingMessage>> groups, List<CharSequence> senders) {
+ private void createGroupViews(List<List<MessagingMessage>> groups,
+ List<Notification.Person> senders) {
mGroups.clear();
for (int groupIndex = 0; groupIndex < groups.size(); groupIndex++) {
List<MessagingMessage> group = groups.get(groupIndex);
@@ -314,8 +339,8 @@
private void findGroups(List<MessagingMessage> historicMessages,
List<MessagingMessage> messages, List<List<MessagingMessage>> groups,
- List<CharSequence> senders) {
- CharSequence currentSender = null;
+ List<Notification.Person> senders) {
+ CharSequence currentSenderKey = null;
List<MessagingMessage> currentGroup = null;
int histSize = historicMessages.size();
for (int i = 0; i < histSize + messages.size(); i++) {
@@ -326,35 +351,23 @@
message = messages.get(i - histSize);
}
boolean isNewGroup = currentGroup == null;
- CharSequence sender = message.getMessage().getSender();
- isNewGroup |= !TextUtils.equals(sender, currentSender);
+ Notification.Person sender = message.getMessage().getSenderPerson();
+ CharSequence key = sender == null ? null
+ : sender.getKey() == null ? sender.getName() : sender.getKey();
+ isNewGroup |= !TextUtils.equals(key, currentSenderKey);
if (isNewGroup) {
currentGroup = new ArrayList<>();
groups.add(currentGroup);
+ if (sender == null) {
+ sender = mUser;
+ }
senders.add(sender);
- currentSender = sender;
+ currentSenderKey = key;
}
currentGroup.add(message);
}
}
- private void updateContractedMessage() {
- for (int i = mMessages.size() - 1; i >= 0; i--) {
- MessagingMessage m = mMessages.get(i);
- // Incoming messages have a non-empty sender.
- if (!TextUtils.isEmpty(m.getMessage().getSender())) {
- mContractedMessage = m;
- return;
- }
- }
- if (!mMessages.isEmpty()) {
- // No incoming messages, fall back to outgoing message
- mContractedMessage = mMessages.get(mMessages.size() - 1);
- return;
- }
- mContractedMessage = null;
- }
-
/**
* Creates new messages, reusing existing ones if they are available.
*
@@ -418,7 +431,7 @@
continue;
}
MessagingPropertyAnimator.fadeIn(group.getAvatar());
- MessagingPropertyAnimator.fadeIn(group.getSender());
+ MessagingPropertyAnimator.fadeIn(group.getSenderView());
MessagingPropertyAnimator.startLocalTranslationFrom(group,
group.getHeight(), LINEAR_OUT_SLOW_IN);
}
@@ -430,10 +443,6 @@
}
}
- public View getContractedMessage() {
- return mContractedMessage;
- }
-
public MessagingLinearLayout getMessagingLinearLayout() {
return mMessagingLinearLayout;
}
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 7635a72..6f2246a 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -22,9 +22,7 @@
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcel;
@@ -504,7 +502,7 @@
}
private void onCollapsedChanged(boolean isCollapsed) {
- notifyViewAccessibilityStateChangedIfNeeded(
+ notifyAccessibilityStateChanged(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
if (mScrollIndicatorDrawable != null) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index b3f66e9..77d0617 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -84,7 +84,7 @@
"android_view_VelocityTracker.cpp",
"android_text_AndroidCharacter.cpp",
"android_text_Hyphenator.cpp",
- "android_text_MeasuredText.cpp",
+ "android_text_MeasuredParagraph.cpp",
"android_text_StaticLayout.cpp",
"android_os_Debug.cpp",
"android_os_GraphicsEnvironment.cpp",
@@ -97,7 +97,6 @@
"android_os_MessageQueue.cpp",
"android_os_Parcel.cpp",
"android_os_SELinux.cpp",
- "android_os_seccomp.cpp",
"android_os_SharedMemory.cpp",
"android_os_SystemClock.cpp",
"android_os_SystemProperties.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 6d7fe05..068197a 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -166,7 +166,6 @@
extern int register_android_os_SELinux(JNIEnv* env);
extern int register_android_os_VintfObject(JNIEnv *env);
extern int register_android_os_VintfRuntimeInfo(JNIEnv *env);
-extern int register_android_os_seccomp(JNIEnv* env);
extern int register_android_os_SystemProperties(JNIEnv *env);
extern int register_android_os_SystemClock(JNIEnv* env);
extern int register_android_os_Trace(JNIEnv* env);
@@ -178,7 +177,7 @@
extern int register_android_net_NetworkUtils(JNIEnv* env);
extern int register_android_text_AndroidCharacter(JNIEnv *env);
extern int register_android_text_Hyphenator(JNIEnv *env);
-extern int register_android_text_MeasuredText(JNIEnv* env);
+extern int register_android_text_MeasuredParagraph(JNIEnv* env);
extern int register_android_text_StaticLayout(JNIEnv *env);
extern int register_android_opengl_classes(JNIEnv *env);
extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
@@ -1342,7 +1341,7 @@
REG_JNI(register_android_content_XmlBlock),
REG_JNI(register_android_text_AndroidCharacter),
REG_JNI(register_android_text_Hyphenator),
- REG_JNI(register_android_text_MeasuredText),
+ REG_JNI(register_android_text_MeasuredParagraph),
REG_JNI(register_android_text_StaticLayout),
REG_JNI(register_android_view_InputDevice),
REG_JNI(register_android_view_KeyCharacterMap),
@@ -1427,7 +1426,6 @@
REG_JNI(register_android_os_GraphicsEnvironment),
REG_JNI(register_android_os_MessageQueue),
REG_JNI(register_android_os_SELinux),
- REG_JNI(register_android_os_seccomp),
REG_JNI(register_android_os_Trace),
REG_JNI(register_android_os_UEventObserver),
REG_JNI(register_android_net_LocalSocketImpl),
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 79aa5ac..685fcaf 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -237,10 +237,22 @@
// Create the codec.
NinePatchPeeker peeker;
- std::unique_ptr<SkAndroidCodec> codec = SkAndroidCodec::MakeFromStream(
- std::move(stream), &peeker);
- if (!codec.get()) {
- return nullObjectReturn("SkAndroidCodec::MakeFromStream returned null");
+ std::unique_ptr<SkAndroidCodec> codec;
+ {
+ SkCodec::Result result;
+ std::unique_ptr<SkCodec> c = SkCodec::MakeFromStream(std::move(stream), &result,
+ &peeker);
+ if (!c) {
+ SkString msg;
+ msg.printf("Failed to create image decoder with message '%s'",
+ SkCodec::ResultToString(result));
+ return nullObjectReturn(msg.c_str());
+ }
+
+ codec = SkAndroidCodec::MakeFromCodec(std::move(c));
+ if (!codec) {
+ return nullObjectReturn("SkAndroidCodec::MakeFromCodec returned null");
+ }
}
// Do not allow ninepatch decodes to 565. In the past, decodes to 565
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index c237564..249202a 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -46,7 +46,7 @@
static jmethodID gPoint_constructorMethodID;
static jmethodID gIncomplete_constructorMethodID;
static jmethodID gCorrupt_constructorMethodID;
-static jmethodID gCallback_onExceptionMethodID;
+static jmethodID gCallback_onPartialImageMethodID;
static jmethodID gPostProcess_postProcessMethodID;
static jmethodID gCanvas_constructorMethodID;
static jmethodID gCanvas_releaseMethodID;
@@ -67,8 +67,8 @@
kOpaque = -1,
};
- std::unique_ptr<SkAndroidCodec> mCodec;
NinePatchPeeker mPeeker;
+ std::unique_ptr<SkAndroidCodec> mCodec;
};
static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) {
@@ -77,11 +77,27 @@
return nullptr;
}
std::unique_ptr<ImageDecoder> decoder(new ImageDecoder);
- decoder->mCodec = SkAndroidCodec::MakeFromStream(std::move(stream), &decoder->mPeeker);
+ SkCodec::Result result;
+ auto codec = SkCodec::MakeFromStream(std::move(stream), &result, &decoder->mPeeker);
+ if (!codec) {
+ switch (result) {
+ case SkCodec::kIncompleteInput:
+ env->ThrowNew(gIncomplete_class, "Incomplete input");
+ break;
+ default:
+ SkString msg;
+ msg.printf("Failed to create image decoder with message '%s'",
+ SkCodec::ResultToString(result));
+ doThrowIOE(env, msg.c_str());
+ break;
+ }
+
+ return nullptr;
+ }
+
+ decoder->mCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
if (!decoder->mCodec.get()) {
- // FIXME: (b/71578461) Use the error message from
- // SkCodec::MakeFromStream to report a more informative error message.
- doThrowIOE(env, "Failed to create an SkCodec");
+ doThrowIOE(env, "Could not create AndroidCodec");
return nullptr;
}
@@ -160,11 +176,6 @@
return native_create(env, std::move(stream));
}
-static bool supports_any_down_scale(const SkAndroidCodec* codec) {
- return codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP;
-}
-
-// This method should never return null. Instead, it should throw an exception.
static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
jobject jcallback, jobject jpostProcess,
jint desiredWidth, jint desiredHeight, jobject jsubset,
@@ -173,33 +184,14 @@
jboolean asAlphaMask) {
auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
SkAndroidCodec* codec = decoder->mCodec.get();
- SkImageInfo decodeInfo = codec->getInfo();
- bool scale = false;
- int sampleSize = 1;
- if (desiredWidth != decodeInfo.width() || desiredHeight != decodeInfo.height()) {
- bool match = false;
- if (desiredWidth < decodeInfo.width() && desiredHeight < decodeInfo.height()) {
- if (supports_any_down_scale(codec)) {
- match = true;
- decodeInfo = decodeInfo.makeWH(desiredWidth, desiredHeight);
- } else {
- int sampleX = decodeInfo.width() / desiredWidth;
- int sampleY = decodeInfo.height() / desiredHeight;
- sampleSize = std::min(sampleX, sampleY);
- SkISize sampledSize = codec->getSampledDimensions(sampleSize);
- decodeInfo = decodeInfo.makeWH(sampledSize.width(), sampledSize.height());
- if (decodeInfo.width() == desiredWidth && decodeInfo.height() == desiredHeight) {
- match = true;
- }
- }
- }
- if (!match) {
- scale = true;
- if (requireUnpremul && kOpaque_SkAlphaType != decodeInfo.alphaType()) {
- doThrowISE(env, "Cannot scale unpremultiplied pixels!");
- return nullptr;
- }
- }
+ const SkISize desiredSize = SkISize::Make(desiredWidth, desiredHeight);
+ SkISize decodeSize = desiredSize;
+ const int sampleSize = codec->computeSampleSize(&decodeSize);
+ const bool scale = desiredSize != decodeSize;
+ SkImageInfo decodeInfo = codec->getInfo().makeWH(decodeSize.width(), decodeSize.height());
+ if (scale && requireUnpremul && kOpaque_SkAlphaType != decodeInfo.alphaType()) {
+ doThrowISE(env, "Cannot scale unpremultiplied pixels!");
+ return nullptr;
}
switch (decodeInfo.alphaType()) {
@@ -276,7 +268,7 @@
SkAndroidCodec::AndroidOptions options;
options.fSampleSize = sampleSize;
auto result = codec->getAndroidPixels(decodeInfo, bm.getPixels(), bm.rowBytes(), &options);
- jobject jexception = env->ExceptionOccurred();
+ jthrowable jexception = env->ExceptionOccurred();
if (jexception) {
env->ExceptionClear();
}
@@ -287,12 +279,14 @@
break;
case SkCodec::kIncompleteInput:
if (jcallback && !jexception) {
- jexception = env->NewObject(gIncomplete_class, gIncomplete_constructorMethodID);
+ jexception = (jthrowable) env->NewObject(gIncomplete_class,
+ gIncomplete_constructorMethodID);
}
break;
case SkCodec::kErrorInInput:
if (jcallback && !jexception) {
- jexception = env->NewObject(gCorrupt_class, gCorrupt_constructorMethodID);
+ jexception = (jthrowable) env->NewObject(gCorrupt_class,
+ gCorrupt_constructorMethodID);
}
break;
default:
@@ -303,9 +297,14 @@
}
if (jexception) {
- // FIXME: Do not provide a way for the client to force the method to return null.
- if (!env->CallBooleanMethod(jcallback, gCallback_onExceptionMethodID, jexception) ||
- env->ExceptionCheck()) {
+ bool throwException = !env->CallBooleanMethod(jcallback, gCallback_onPartialImageMethodID,
+ jexception);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+
+ if (throwException) {
+ env->Throw(jexception);
return nullptr;
}
}
@@ -512,7 +511,7 @@
{ "nCreate", "([BII)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
{ "nCreate", "(Ljava/io/InputStream;[B)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
{ "nCreate", "(Ljava/io/FileDescriptor;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
- { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder$OnExceptionListener;Landroid/graphics/PostProcess;IILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;",
+ { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder$OnPartialImageListener;Landroid/graphics/PostProcess;IILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;",
(void*) ImageDecoder_nDecodeBitmap },
{ "nGetSampledSize","(JI)Landroid/graphics/Point;", (void*) ImageDecoder_nGetSampledSize },
{ "nGetPadding", "(JLandroid/graphics/Rect;)V", (void*) ImageDecoder_nGetPadding },
@@ -533,8 +532,8 @@
gCorrupt_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder$CorruptException"));
gCorrupt_constructorMethodID = GetMethodIDOrDie(env, gCorrupt_class, "<init>", "()V");
- jclass callback_class = FindClassOrDie(env, "android/graphics/ImageDecoder$OnExceptionListener");
- gCallback_onExceptionMethodID = GetMethodIDOrDie(env, callback_class, "onException", "(Ljava/io/IOException;)Z");
+ jclass callback_class = FindClassOrDie(env, "android/graphics/ImageDecoder$OnPartialImageListener");
+ gCallback_onPartialImageMethodID = GetMethodIDOrDie(env, callback_class, "onPartialImage", "(Ljava/io/IOException;)Z");
jclass postProcess_class = FindClassOrDie(env, "android/graphics/PostProcess");
gPostProcess_postProcessMethodID = GetMethodIDOrDie(env, postProcess_class, "postProcess", "(Landroid/graphics/Canvas;II)I");
diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
index 1eeea51..1659168 100644
--- a/core/jni/android_os_VintfObject.cpp
+++ b/core/jni/android_os_VintfObject.cpp
@@ -146,8 +146,8 @@
return nullptr;
}
jobject jMap = env->NewObject(gHashMapClazz, gHashMapInit);
- for (const Vndk &vndk : manifest->vndks()) {
- std::string key = to_string(vndk.versionRange());
+ for (const auto &vndk : manifest->vendorNdks()) {
+ std::string key = vndk.version();
env->CallObjectMethod(jMap, gHashMapPut,
env->NewStringUTF(key.c_str()), toJavaStringArray(env, vndk.libraries()));
}
diff --git a/core/jni/android_os_seccomp.cpp b/core/jni/android_os_seccomp.cpp
deleted file mode 100644
index 06e2a16..0000000
--- a/core/jni/android_os_seccomp.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2017 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 "core_jni_helpers.h"
-#include <nativehelper/JniConstants.h>
-#include "utils/Log.h"
-#include <selinux/selinux.h>
-
-#include "seccomp_policy.h"
-
-static void Seccomp_setPolicy(JNIEnv* /*env*/) {
- if (security_getenforce() == 0) {
- ALOGI("seccomp disabled by setenforce 0");
- return;
- }
-
- if (!set_seccomp_filter()) {
- ALOGE("Failed to set seccomp policy - killing");
- exit(1);
- }
-}
-
-static const JNINativeMethod method_table[] = {
- NATIVE_METHOD(Seccomp, setPolicy, "()V"),
-};
-
-namespace android {
-
-int register_android_os_seccomp(JNIEnv* env) {
- return android::RegisterMethodsOrDie(env, "android/os/Seccomp",
- method_table, NELEM(method_table));
-}
-
-}
diff --git a/core/jni/android_text_MeasuredText.cpp b/core/jni/android_text_MeasuredParagraph.cpp
similarity index 83%
rename from core/jni/android_text_MeasuredText.cpp
rename to core/jni/android_text_MeasuredParagraph.cpp
index af9d131..bdae0b2 100644
--- a/core/jni/android_text_MeasuredText.cpp
+++ b/core/jni/android_text_MeasuredParagraph.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "MeasuredText"
+#define LOG_TAG "MeasuredParagraph"
#include "ScopedIcuLocale.h"
#include "unicode/locid.h"
@@ -49,7 +49,7 @@
return reinterpret_cast<Paint*>(ptr);
}
-static inline minikin::MeasuredText* toMeasuredText(jlong ptr) {
+static inline minikin::MeasuredText* toMeasuredParagraph(jlong ptr) {
return reinterpret_cast<minikin::MeasuredText*>(ptr);
}
@@ -57,8 +57,8 @@
return reinterpret_cast<jlong>(ptr);
}
-static void releaseMeasuredText(jlong measuredTextPtr) {
- delete toMeasuredText(measuredTextPtr);
+static void releaseMeasuredParagraph(jlong measuredTextPtr) {
+ delete toMeasuredParagraph(measuredTextPtr);
}
// Regular JNI
@@ -84,7 +84,7 @@
}
// Regular JNI
-static jlong nBuildNativeMeasuredText(JNIEnv* env, jclass /* unused */, jlong builderPtr,
+static jlong nBuildNativeMeasuredParagraph(JNIEnv* env, jclass /* unused */, jlong builderPtr,
jcharArray javaText) {
ScopedCharArrayRO text(env, javaText);
const minikin::U16StringPiece textBuffer(text.get(), text.size());
@@ -100,23 +100,23 @@
// CriticalNative
static jlong nGetReleaseFunc() {
- return toJLong(&releaseMeasuredText);
+ return toJLong(&releaseMeasuredParagraph);
}
static const JNINativeMethod gMethods[] = {
- // MeasuredTextBuilder native functions.
+ // MeasuredParagraphBuilder native functions.
{"nInitBuilder", "()J", (void*) nInitBuilder},
{"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun},
{"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun},
- {"nBuildNativeMeasuredText", "(J[C)J", (void*) nBuildNativeMeasuredText},
+ {"nBuildNativeMeasuredParagraph", "(J[C)J", (void*) nBuildNativeMeasuredParagraph},
{"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
- // MeasuredText native functions.
+ // MeasuredParagraph native functions.
{"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc}, // Critical Natives
};
-int register_android_text_MeasuredText(JNIEnv* env) {
- return RegisterMethodsOrDie(env, "android/text/MeasuredText", gMethods, NELEM(gMethods));
+int register_android_text_MeasuredParagraph(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "android/text/MeasuredParagraph", gMethods, NELEM(gMethods));
}
}
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index b5c23df..682dc873 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -174,7 +174,7 @@
// Inputs
"[C" // text
- "J" // MeasuredText ptr.
+ "J" // MeasuredParagraph ptr.
"I" // length
"F" // firstWidth
"I" // firstWidthLineCount
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 5e5d59b..0ef5445 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -72,7 +72,6 @@
// Implements SkMallocPixelRef::ReleaseProc, to delete the screenshot on unref.
void DeleteScreenshot(void* addr, void* context) {
- SkASSERT(addr == ((ScreenshotClient*) context)->getPixels());
delete ((ScreenshotClient*) context);
}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 1407ae4..13e6fcd 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -53,6 +53,7 @@
#include <private/android_filesystem_config.h>
#include <utils/String8.h>
#include <selinux/android.h>
+#include <seccomp_policy.h>
#include <processgroup/processgroup.h>
#include "core_jni_helpers.h"
@@ -76,6 +77,8 @@
static jclass gZygoteClass;
static jmethodID gCallPostForkChildHooks;
+static bool g_is_security_enforced = true;
+
// Must match values in com.android.internal.os.Zygote.
enum MountExternalKind {
MOUNT_EXTERNAL_NONE = 0,
@@ -229,6 +232,20 @@
mallopt(M_DECAY_TIME, 1);
}
+static void SetUpSeccompFilter(uid_t uid) {
+ if (!g_is_security_enforced) {
+ ALOGI("seccomp disabled by setenforce 0");
+ return;
+ }
+
+ // Apply system or app filter based on uid.
+ if (getuid() >= AID_APP_START) {
+ set_app_seccomp_filter();
+ } else {
+ set_system_seccomp_filter();
+ }
+}
+
static void EnableKeepCapabilities(JNIEnv* env) {
int rc = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
if (rc == -1) {
@@ -541,6 +558,11 @@
RuntimeAbort(env, __LINE__, "Call to sigprocmask(SIG_UNBLOCK, { SIGCHLD }) failed.");
}
+ // Must be called when the new process still has CAP_SYS_ADMIN. The other alternative is to
+ // call prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
+ // b/71859146).
+ SetUpSeccompFilter(uid);
+
// Keep capabilities across UID change, unless we're staying root.
if (uid != 0) {
EnableKeepCapabilities(env);
@@ -698,6 +720,12 @@
namespace android {
+static void com_android_internal_os_Zygote_nativeSecurityInit(JNIEnv*, jclass) {
+ // security_getenforce is not allowed on app process. Initialize and cache the value before
+ // zygote forks.
+ g_is_security_enforced = security_getenforce();
+}
+
static void com_android_internal_os_Zygote_nativePreApplicationInit(JNIEnv*, jclass) {
PreApplicationInit();
}
@@ -835,6 +863,8 @@
}
static const JNINativeMethod gMethods[] = {
+ { "nativeSecurityInit", "()V",
+ (void *) com_android_internal_os_Zygote_nativeSecurityInit },
{ "nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[ILjava/lang/String;Ljava/lang/String;)I",
(void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
diff --git a/core/proto/android/app/statusbarmanager.proto b/core/proto/android/app/statusbarmanager.proto
new file mode 100644
index 0000000..3d1447a
--- /dev/null
+++ b/core/proto/android/app/statusbarmanager.proto
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package android.app;
+option java_multiple_files = true;
+
+message StatusBarManagerProto {
+ enum WindowState {
+ WINDOW_STATE_SHOWING = 0;
+ WINDOW_STATE_HIDING = 1;
+ WINDOW_STATE_HIDDEN = 2;
+ }
+ enum TransientWindowState {
+ TRANSIENT_BAR_NONE = 0;
+ TRANSIENT_BAR_SHOW_REQUESTED = 1;
+ TRANSIENT_BAR_SHOWING = 2;
+ TRANSIENT_BAR_HIDING = 3;
+ }
+}
diff --git a/core/proto/android/content/activityinfo.proto b/core/proto/android/content/activityinfo.proto
new file mode 100644
index 0000000..012752a
--- /dev/null
+++ b/core/proto/android/content/activityinfo.proto
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package android.content;
+option java_multiple_files = true;
+
+message ActivityInfoProto {
+ enum ScreenOrientation {
+ SCREEN_ORIENTATION_UNSET = -2;
+ SCREEN_ORIENTATION_UNSPECIFIED = -1;
+ SCREEN_ORIENTATION_LANDSCAPE = 0;
+ SCREEN_ORIENTATION_PORTRAIT = 1;
+ SCREEN_ORIENTATION_USER = 2;
+ SCREEN_ORIENTATION_BEHIND = 3;
+ SCREEN_ORIENTATION_SENSOR = 4;
+ SCREEN_ORIENTATION_NOSENSOR = 5;
+ SCREEN_ORIENTATION_SENSOR_LANDSCAPE = 6;
+ SCREEN_ORIENTATION_SENSOR_PORTRAIT = 7;
+ SCREEN_ORIENTATION_REVERSE_LANDSCAPE = 8;
+ SCREEN_ORIENTATION_REVERSE_PORTRAIT = 9;
+ SCREEN_ORIENTATION_FULL_SENSOR = 10;
+ SCREEN_ORIENTATION_USER_LANDSCAPE = 11;
+ SCREEN_ORIENTATION_USER_PORTRAIT = 12;
+ SCREEN_ORIENTATION_FULL_USER = 13;
+ SCREEN_ORIENTATION_LOCKED = 14;
+ }
+}
+
diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto
index 9999b4e..331f80f 100644
--- a/core/proto/android/os/batterystats.proto
+++ b/core/proto/android/os/batterystats.proto
@@ -674,14 +674,13 @@
// needed:
// top > foreground service > foreground > background > top sleeping > heavy weight > cache
enum State {
- // Time this uid has any processes in the top state (or above such as
- // persistent).
+ // Time this uid has any processes in the top state.
PROCESS_STATE_TOP = 0;
- // Time this uid has any process with a started out bound foreground
- // service, but none in the "top" state.
+ // Time this uid has any process with a started foreground service, but
+ // none in the "top" state.
PROCESS_STATE_FOREGROUND_SERVICE = 1;
- // Time this uid has any process in an active foreground state, but none
- // in the "top sleeping" or better state.
+ // Time this uid has any process in an active foreground state, but none in the
+ // "foreground service" or better state. Persistent and other foreground states go here.
PROCESS_STATE_FOREGROUND = 2;
// Time this uid has any process in an active background state, but none
// in the "foreground" or better state.
diff --git a/core/proto/android/os/system_properties.proto b/core/proto/android/os/system_properties.proto
index 59582ec..07b9ad0 100644
--- a/core/proto/android/os/system_properties.proto
+++ b/core/proto/android/os/system_properties.proto
@@ -228,8 +228,9 @@
}
optional PmDexopt pm_dexopt = 20;
+ // Read only properites on the device.
message Ro {
- optional int32 adb_secure = 1;
+ optional bool adb_secure = 1;
optional string arch = 2;
optional bool audio_ignore_effects = 3;
optional bool audio_monitorRotation = 4;
@@ -262,21 +263,29 @@
}
optional Boot boot = 7;
+ // boot.img's properties.
message BootImage {
+ // When the boot.img is built.
optional string build_date = 1;
- optional int32 build_date_utc = 2;
+ // UTC timestamp of build date.
+ optional int64 build_date_utc = 2;
+ // Android Build fingerprint of the build, e.g.
+ // google/marlin/marlin:P/MASTER/android-build/dev-keys
optional string build_fingerprint = 3;
// Next Tag: 4
}
optional BootImage bootimage = 8;
+ // Version of bootloader on device.
optional string bootloader = 9;
+ // Kernel bootmode, e.g. charger.
optional string bootmode = 10;
+ // Android Platform build metadata.
message Build {
optional string date = 1;
- optional int32 date_utc = 2;
+ optional int64 date_utc = 2;
optional string description = 3;
optional string display_id = 4;
optional string host = 5;
@@ -304,7 +313,7 @@
}
optional Build build = 11;
- optional int32 camera_notify_nfc = 12;
+ optional bool camera_notify_nfc = 12;
optional string carrier = 13;
optional bool com_android_dataroaming = 14;
optional bool com_android_prov_mobiledata = 15;
@@ -327,7 +336,7 @@
optional string crypto_state = 21;
optional string crypto_type = 22;
optional string dalvik_vm_native_bridge = 23;
- optional bool debuggable = 24;
+ optional bool debuggable = 24;
optional string frp_pst = 25;
optional string gfx_driver_0 = 26;
@@ -426,7 +435,7 @@
message Vendor {
optional string build_date = 1;
- optional int32 build_date_utc = 2;
+ optional int64 build_date_utc = 2;
optional string build_fingerprint = 3;
}
optional Vendor vendor = 41;
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index f5d098c..3ffb254 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -388,8 +388,10 @@
optional SettingProto enable_deletion_helper_no_threshold_toggle = 340;
optional SettingProto notification_snooze_options = 341;
optional SettingProto enable_gnss_raw_meas_full_tracking = 346;
+ optional SettingProto zram_enabled = 347;
+ optional SettingProto enable_smart_replies_in_notifications = 348;
- // Next tag = 347;
+ // Next tag = 349;
}
message SecureSettingsProto {
diff --git a/core/proto/android/server/appwindowthumbnail.proto b/core/proto/android/server/appwindowthumbnail.proto
new file mode 100644
index 0000000..e67b854
--- /dev/null
+++ b/core/proto/android/server/appwindowthumbnail.proto
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+import "frameworks/base/core/proto/android/server/surfaceanimator.proto";
+
+package com.android.server.wm.proto;
+option java_multiple_files = true;
+
+/**
+ * Represents a {@link com.android.server.wm.AppWindowThumbnail} object.
+ */
+message AppWindowThumbnailProto {
+ optional int32 width = 1;
+ optional int32 height = 2;
+ optional SurfaceAnimatorProto surface_animator = 3;
+}
\ No newline at end of file
diff --git a/core/proto/android/server/forceappstandbytracker.proto b/core/proto/android/server/forceappstandbytracker.proto
index 8753bf7..c9f7d52 100644
--- a/core/proto/android/server/forceappstandbytracker.proto
+++ b/core/proto/android/server/forceappstandbytracker.proto
@@ -41,4 +41,13 @@
// Packages that are disallowed OP_RUN_ANY_IN_BACKGROUND.
repeated RunAnyInBackgroundRestrictedPackages run_any_in_background_restricted_packages = 5;
+
+ // Whether device is a small battery device
+ optional bool is_small_battery_device = 6;
+
+ // Whether force app standby for small battery device setting is enabled
+ optional bool force_all_apps_standby_for_small_battery = 7;
+
+ // Whether device is charging
+ optional bool is_charging = 8;
}
diff --git a/core/proto/android/server/surfaceanimator.proto b/core/proto/android/server/surfaceanimator.proto
new file mode 100644
index 0000000..60713d7
--- /dev/null
+++ b/core/proto/android/server/surfaceanimator.proto
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+import "frameworks/base/core/proto/android/view/surfacecontrol.proto";
+
+package com.android.server.wm.proto;
+option java_multiple_files = true;
+
+/**
+ * Represents a {@link com.android.server.wm.SurfaceAnimator} object.
+ */
+message SurfaceAnimatorProto {
+ optional string animation_adapter = 1;
+ optional .android.view.SurfaceControlProto leash = 2;
+ optional bool animation_start_delayed = 3;
+}
\ No newline at end of file
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 12aca78..71f33c7 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -16,10 +16,15 @@
syntax = "proto2";
+import "frameworks/base/core/proto/android/app/statusbarmanager.proto";
+import "frameworks/base/core/proto/android/content/activityinfo.proto";
import "frameworks/base/core/proto/android/content/configuration.proto";
import "frameworks/base/core/proto/android/graphics/rect.proto";
+import "frameworks/base/core/proto/android/server/appwindowthumbnail.proto";
+import "frameworks/base/core/proto/android/server/surfaceanimator.proto";
import "frameworks/base/core/proto/android/view/displaycutout.proto";
import "frameworks/base/core/proto/android/view/displayinfo.proto";
+import "frameworks/base/core/proto/android/view/surface.proto";
import "frameworks/base/core/proto/android/view/windowlayoutparams.proto";
package com.android.server.wm.proto;
@@ -47,8 +52,62 @@
repeated IdentifierProto windows = 3;
}
+message BarControllerProto {
+ optional .android.app.StatusBarManagerProto.WindowState state = 1;
+ optional .android.app.StatusBarManagerProto.TransientWindowState transient_state = 2;
+}
+
+message WindowOrientationListenerProto {
+ optional bool enabled = 1;
+ optional .android.view.SurfaceProto.Rotation rotation = 2;
+}
+
+message KeyguardServiceDelegateProto {
+ optional bool showing = 1;
+ optional bool occluded = 2;
+ optional bool secure = 3;
+ enum ScreenState {
+ SCREEN_STATE_OFF = 0;
+ SCREEN_STATE_TURNING_ON = 1;
+ SCREEN_STATE_ON = 2;
+ SCREEN_STATE_TURNING_OFF = 3;
+ }
+ optional ScreenState screen_state = 4;
+ enum InteractiveState {
+ INTERACTIVE_STATE_SLEEP = 0;
+ INTERACTIVE_STATE_WAKING = 1;
+ INTERACTIVE_STATE_AWAKE = 2;
+ INTERACTIVE_STATE_GOING_TO_SLEEP = 3;
+ }
+ optional InteractiveState interactive_state = 5;
+}
+
/* represents PhoneWindowManager */
message WindowManagerPolicyProto {
+ optional int32 last_system_ui_flags = 1;
+ enum UserRotationMode {
+ USER_ROTATION_FREE = 0;
+ USER_ROTATION_LOCKED = 1;
+ }
+ optional UserRotationMode rotation_mode = 2;
+ optional .android.view.SurfaceProto.Rotation rotation = 3;
+ optional .android.content.ActivityInfoProto.ScreenOrientation orientation = 4;
+ optional bool screen_on_fully = 5;
+ optional bool keyguard_draw_complete = 6;
+ optional bool window_manager_draw_complete = 7;
+ optional string focused_app_token = 8;
+ optional IdentifierProto focused_window = 9;
+ optional IdentifierProto top_fullscreen_opaque_window = 10;
+ optional IdentifierProto top_fullscreen_opaque_or_dimming_window = 11;
+ optional bool keyguard_occluded = 12;
+ optional bool keyguard_occluded_changed = 13;
+ optional bool keyguard_occluded_pending = 14;
+ optional bool force_status_bar = 15;
+ optional bool force_status_bar_from_keyguard = 16;
+ optional BarControllerProto status_bar = 17;
+ optional BarControllerProto navigation_bar = 18;
+ optional WindowOrientationListenerProto orientation_listener = 19;
+ optional KeyguardServiceDelegateProto keyguard_delegate = 20;
}
/* represents AppTransition */
@@ -128,6 +187,12 @@
optional bool fills_parent = 4;
optional .android.graphics.RectProto bounds = 5;
optional bool animation_background_surface_is_dimming = 6;
+ optional bool defer_removal = 7;
+ optional float minimize_amount = 8;
+ optional bool adjusted_for_ime = 9;
+ optional float adjust_ime_amount = 10;
+ optional float adjust_divider_amount = 11;
+ optional .android.graphics.RectProto adjusted_bounds = 12;
}
/* represents Task */
@@ -138,6 +203,7 @@
optional bool fills_parent = 4;
optional .android.graphics.RectProto bounds = 5;
optional .android.graphics.RectProto temp_inset_bounds = 6;
+ optional bool defer_removal = 7;
}
/* represents AppWindowToken */
@@ -145,6 +211,27 @@
/* obtained from ActivityRecord */
optional string name = 1;
optional WindowTokenProto window_token = 2;
+ optional bool last_surface_showing = 3;
+ optional bool is_waiting_for_transition_start = 4;
+ optional bool is_really_animating = 5;
+ optional AppWindowThumbnailProto thumbnail = 6;
+ optional bool fills_parent = 7;
+ optional bool app_stopped = 8;
+ optional bool hidden_requested = 9;
+ optional bool client_hidden = 10;
+ optional bool defer_hiding_client = 11;
+ optional bool reported_drawn = 12;
+ optional bool reported_visible = 13;
+ optional int32 num_interesting_windows = 14;
+ optional int32 num_drawn_windows = 15;
+ optional bool all_drawn = 16;
+ optional bool last_all_drawn = 17;
+ optional bool removed = 18;
+ optional IdentifierProto starting_window = 19;
+ optional bool starting_displayed = 20;
+ optional bool starting_moved = 21;
+ optional bool hidden_set_from_transferred_starting_window = 22;
+ repeated .android.graphics.RectProto frozen_bounds = 23;
}
/* represents WindowToken */
@@ -152,6 +239,9 @@
optional WindowContainerProto window_container = 1;
optional int32 hash_code = 2;
repeated WindowStateProto windows = 3;
+ optional bool hidden = 4;
+ optional bool waiting_to_show = 5;
+ optional bool paused = 6;
}
/* represents WindowState */
@@ -234,6 +324,7 @@
optional ConfigurationContainerProto configuration_container = 1;
optional int32 orientation = 2;
optional bool visible = 3;
+ optional SurfaceAnimatorProto surface_animator = 4;
}
/* represents ConfigurationContainer */
diff --git a/core/proto/android/server/windowmanagertrace.proto b/core/proto/android/server/windowmanagertrace.proto
index 0c65bb2..d96953e 100644
--- a/core/proto/android/server/windowmanagertrace.proto
+++ b/core/proto/android/server/windowmanagertrace.proto
@@ -16,11 +16,7 @@
syntax = "proto2";
-import "frameworks/base/core/proto/android/content/configuration.proto";
-import "frameworks/base/core/proto/android/graphics/rect.proto";
import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
-import "frameworks/base/core/proto/android/view/displayinfo.proto";
-import "frameworks/base/core/proto/android/view/windowlayoutparams.proto";
package com.android.server.wm.proto;
diff --git a/core/proto/android/service/diskstats.proto b/core/proto/android/service/diskstats.proto
index f725e8a..3c7a0e3 100644
--- a/core/proto/android/service/diskstats.proto
+++ b/core/proto/android/service/diskstats.proto
@@ -43,6 +43,8 @@
optional EncryptionType encryption = 5;
// Cached values of folder sizes, etc.
optional DiskStatsCachedValuesProto cached_folder_sizes = 6;
+ // Average write speed of storaged benchmark for last 24 hours
+ optional int32 benchmarked_write_speed_kbps = 7;
}
message DiskStatsCachedValuesProto {
diff --git a/core/proto/android/util/log.proto b/core/proto/android/util/log.proto
index 30ff412..fd4fa9e 100644
--- a/core/proto/android/util/log.proto
+++ b/core/proto/android/util/log.proto
@@ -57,10 +57,10 @@
optional uint32 tag_index = 6;
message Elem {
- // must be sync with liblog log/log.h
+ // must be sync with AOSP liblog's log.h
enum Type {
- EVENT_TYPE_LIST_STOP = 10; // '\n'
- EVENT_TYPE_UNKNOWN = 63; // '?'
+ EVENT_TYPE_LIST_STOP = 10; // ascii decimal value of char '\n'
+ EVENT_TYPE_UNKNOWN = 63; // ascii decimal value of char '?'
EVENT_TYPE_INT = 0;
EVENT_TYPE_LONG = 1;
diff --git a/core/java/android/os/Seccomp.java b/core/proto/android/view/surface.proto
similarity index 68%
rename from core/java/android/os/Seccomp.java
rename to core/proto/android/view/surface.proto
index f14e93f..8f5f695 100644
--- a/core/java/android/os/Seccomp.java
+++ b/core/proto/android/view/surface.proto
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,11 +14,15 @@
* limitations under the License.
*/
-package android.os;
+syntax = "proto2";
+package android.view;
+option java_multiple_files = true;
-/**
- * @hide
- */
-public final class Seccomp {
- public static final native void setPolicy();
+message SurfaceProto {
+ enum Rotation {
+ ROTATION_0 = 0;
+ ROTATION_90 = 1;
+ ROTATION_180 = 2;
+ ROTATION_270 = 3;
+ }
}
diff --git a/core/java/android/os/Seccomp.java b/core/proto/android/view/surfacecontrol.proto
similarity index 66%
copy from core/java/android/os/Seccomp.java
copy to core/proto/android/view/surfacecontrol.proto
index f14e93f..9288b4f 100644
--- a/core/java/android/os/Seccomp.java
+++ b/core/proto/android/view/surfacecontrol.proto
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,11 +14,15 @@
* limitations under the License.
*/
-package android.os;
+syntax = "proto2";
+package android.view;
+
+option java_multiple_files = true;
/**
- * @hide
+ * Represents a {@link android.view.SurfaceControl} object.
*/
-public final class Seccomp {
- public static final native void setPolicy();
+message SurfaceControlProto {
+ optional int32 hash_code = 1;
+ optional string name = 2;
}
diff --git a/core/proto/android/view/windowlayoutparams.proto b/core/proto/android/view/windowlayoutparams.proto
index b81cd1f..f079e1e 100644
--- a/core/proto/android/view/windowlayoutparams.proto
+++ b/core/proto/android/view/windowlayoutparams.proto
@@ -58,7 +58,6 @@
optional NeedsMenuState needs_menu_key = 22;
optional .android.view.DisplayProto.ColorMode color_mode = 23;
optional uint32 flags = 24;
- optional uint64 flags_extra = 25;
optional uint32 private_flags = 26;
optional uint32 system_ui_visibility_flags = 27;
optional uint32 subtree_system_ui_visibility_flags = 28;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 00e3a4a..d2a22d0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -572,6 +572,11 @@
<protected-broadcast android:name="android.intent.action.GET_RESTRICTION_ENTRIES" />
<protected-broadcast android:name="android.telephony.euicc.action.OTA_STATUS_CHANGED" />
+ <!-- Added in P -->
+ <protected-broadcast android:name="android.app.action.PROFILE_OWNER_CHANGED" />
+ <protected-broadcast android:name="android.app.action.TRANSFER_OWNERSHIP_COMPLETE" />
+ <protected-broadcast android:name="android.app.action.DATA_SHARING_RESTRICTION_CHANGED" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
@@ -2948,13 +2953,14 @@
<!-- Allows an application to collect usage infomation about brightness slider changes.
<p>Not for use by third-party applications.</p>
- TODO: make a System API
- @hide -->
+ @hide
+ @SystemApi -->
<permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|development" />
<!-- Allows an application to modify the display brightness configuration
- @hide -->
+ @hide
+ @SystemApi -->
<permission android:name="android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"
android:protectionLevel="signature|privileged|development" />
@@ -3304,6 +3310,10 @@
<permission android:name="android.permission.BIND_PACKAGE_VERIFIER"
android:protectionLevel="signature" />
+ <!-- @SystemApi @hide Allows an application to mark other applications as harmful -->
+ <permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS"
+ android:protectionLevel="signature|verifier" />
+
<!-- @SystemApi @hide Intent filter verifier needs to have this permission before the
PackageManager will trust it to verify intent filters.
-->
@@ -3390,6 +3400,12 @@
<permission android:name="android.permission.PROVIDE_TRUST_AGENT"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows an application to show a message
+ on the keyguard when asking to dismiss it.
+ @hide For security reasons, this is a platform-only permission. -->
+ <permission android:name="android.permission.SHOW_KEYGUARD_MESSAGE"
+ android:protectionLevel="signature|privileged" />
+
<!-- Allows an application to launch the trust agent settings activity.
@hide -->
<permission android:name="android.permission.LAUNCH_TRUST_AGENT_SETTINGS"
@@ -3676,6 +3692,12 @@
<permission android:name="android.permission.MODIFY_QUIET_MODE"
android:protectionLevel="signature|privileged" />
+ <!-- Allows an application to control remote animations. See
+ {@link ActivityOptions#makeRemoteAnimation}
+ @hide <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"
+ android:protectionLevel="signature|privileged" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
@@ -3869,6 +3891,13 @@
android:excludeFromRecents="true">
</activity>
+ <activity android:name="com.android.internal.app.HarmfulAppWarningActivity"
+ android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
+ android:excludeFromRecents="true"
+ android:process=":ui"
+ android:exported="false">
+ </activity>
+
<receiver android:name="com.android.server.BootReceiver"
android:systemUserOnly="true">
<intent-filter android:priority="1000">
diff --git a/core/res/res/drawable/ic_screenshot.xml b/core/res/res/drawable/ic_screenshot.xml
new file mode 100644
index 0000000..3074b28
--- /dev/null
+++ b/core/res/res/drawable/ic_screenshot.xml
@@ -0,0 +1,33 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M0,0h24v24H0V0z"
+ android:fillColor="#00000000"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17.0,1.0L7.0,1.0C5.9,1.0 5.0,1.9 5.0,3.0l0.0,18.0c0.0,1.1 0.9,2.0 2.0,2.0l10.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L19.0,3.0C19.0,1.9 18.1,1.0 17.0,1.0zM17.0,20.0L7.0,20.0L7.0,4.0l10.0,0.0L17.0,20.0z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13.0,6.0l-4.0,0.0 0.0,4.0 1.5,0.0 0.0,-2.5 2.5,0.0z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.0,18.0l4.0,0.0 0.0,-4.0 -1.5,0.0 0.0,2.5 -2.5,0.0z"/>
+</vector>
diff --git a/core/res/res/drawable/messaging_user.xml b/core/res/res/drawable/messaging_user.xml
new file mode 100644
index 0000000..3137698
--- /dev/null
+++ b/core/res/res/drawable/messaging_user.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="192.0"
+ android:viewportWidth="192.0">
+ <path android:fillColor="#000000"
+ android:fillAlpha="0.125"
+ android:pathData="M96,0C43.01,0 0,43.01 0,96s43.01,96 96,96s96,-43.01 96,-96S148.99,0 96,0z"/>
+ <path android:fillColor="#BDBDBD"
+ android:pathData="M96,85.09c13.28,0 24,-10.72 24,-24c0,-13.28 -10.72,-24 -24,-24s-24,10.72 -24,24C72,74.37 82.72,85.09 96,85.09z"/>
+ <path android:fillColor="#BDBDBD"
+ android:pathData="M96,99.27c-29.33,0 -52.36,14.18 -52.36,27.27c11.09,17.06 30.51,28.36 52.36,28.36s41.27,-11.3 52.36,-28.36C148.36,113.45 125.33,99.27 96,99.27z"/>
+</vector>
diff --git a/core/res/res/layout/autofill_dataset_picker.xml b/core/res/res/layout/autofill_dataset_picker.xml
index 528efca..a88836e 100644
--- a/core/res/res/layout/autofill_dataset_picker.xml
+++ b/core/res/res/layout/autofill_dataset_picker.xml
@@ -24,6 +24,8 @@
android:id="@+id/autofill_dataset_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
+ android:drawSelectorOnTop="true"
+ android:clickable="true"
android:divider="@null"
android:visibility="gone">
</ListView>
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 3a28f4d..20bdf3f 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -41,6 +41,24 @@
android:singleLine="true"
/>
<TextView
+ android:id="@+id/header_text_secondary_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:layout_marginStart="@dimen/notification_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:text="@string/notification_header_divider_symbol"
+ android:visibility="gone"/>
+ <TextView
+ android:id="@+id/header_text_secondary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:layout_marginStart="@dimen/notification_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:visibility="gone"
+ android:singleLine="true"/>
+ <TextView
android:id="@+id/header_text_divider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index 353a1a5..445b19b 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -39,6 +39,10 @@
android:layout_height="@dimen/notification_progress_bar_height"
android:layout_marginTop="@dimen/notification_progress_margin_top"
layout="@layout/notification_template_progress" />
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_content_margin_bottom" />
</LinearLayout>
<include layout="@layout/notification_template_right_icon" />
</FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index 6b1049a..d47bff6 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -56,6 +56,12 @@
android:id="@+id/notification_material_reply_container"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginBottom="@dimen/notification_content_margin_bottom" />
</LinearLayout>
<include layout="@layout/notification_material_action_list" />
</FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_picture.xml b/core/res/res/layout/notification_template_material_big_picture.xml
index e94e646..76c0a67 100644
--- a/core/res/res/layout/notification_template_material_big_picture.xml
+++ b/core/res/res/layout/notification_template_material_big_picture.xml
@@ -64,6 +64,12 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginBottom="@dimen/notification_content_margin_bottom" />
</LinearLayout>
<include layout="@layout/notification_material_action_list" />
</FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index 3c87f92..ac4c052 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -67,6 +67,12 @@
android:id="@+id/notification_material_reply_container"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginBottom="@dimen/notification_content_margin_bottom" />
</LinearLayout>
<include layout="@layout/notification_material_action_list" />
<include layout="@layout/notification_template_right_icon" />
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
index e4c91a4..718cf16 100644
--- a/core/res/res/layout/notification_template_material_inbox.xml
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -119,6 +119,12 @@
android:id="@+id/notification_material_reply_container"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginBottom="@dimen/notification_content_margin_bottom" />
</LinearLayout>
<include layout="@layout/notification_material_action_list" />
<include layout="@layout/notification_template_right_icon" />
diff --git a/core/res/res/layout/notification_template_material_messaging.xml b/core/res/res/layout/notification_template_material_messaging.xml
index f72230b..34f5ae8 100644
--- a/core/res/res/layout/notification_template_material_messaging.xml
+++ b/core/res/res/layout/notification_template_material_messaging.xml
@@ -21,10 +21,7 @@
android:layout_height="wrap_content"
android:tag="messaging"
>
- <include layout="@layout/notification_template_header"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/notification_header_height"
- android:layout_marginEnd="56dp"/>
+ <include layout="@layout/notification_template_header"/>
<LinearLayout
android:id="@+id/notification_action_list_margin_target"
android:layout_width="match_parent"
@@ -39,28 +36,23 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
- android:paddingStart="@dimen/notification_content_margin_start"
- android:paddingEnd="@dimen/notification_content_margin_end"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
android:minHeight="@dimen/notification_min_content_height"
android:layout_marginBottom="@dimen/notification_content_margin_bottom"
android:orientation="vertical"
>
- <include layout="@layout/notification_template_part_line1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
<com.android.internal.widget.MessagingLinearLayout
android:id="@+id/notification_messaging"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
android:spacing="@dimen/notification_messaging_spacing" />
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_content_margin_bottom" />
</LinearLayout>
</LinearLayout>
<include layout="@layout/notification_material_action_list" />
- <include layout="@layout/notification_template_right_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="18dp"
- android:layout_gravity="top|end"/>
+ <include layout="@layout/notification_template_right_icon"/>
</com.android.internal.widget.MessagingLayout>
diff --git a/core/res/res/layout/notification_template_messaging_group.xml b/core/res/res/layout/notification_template_messaging_group.xml
index 8973ceb..4ac308a 100644
--- a/core/res/res/layout/notification_template_messaging_group.xml
+++ b/core/res/res/layout/notification_template_messaging_group.xml
@@ -24,7 +24,7 @@
android:id="@+id/message_icon"
android:layout_width="@dimen/messaging_avatar_size"
android:layout_height="@dimen/messaging_avatar_size"
- android:layout_marginEnd="8dp"
+ android:layout_marginEnd="12dp"
android:scaleType="centerCrop"
android:importantForAccessibility="no" />
<com.android.internal.widget.RemeasuringLinearLayout
@@ -32,19 +32,16 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
+ <com.android.internal.widget.ImageFloatingTextView
+ android:id="@+id/message_name"
+ style="@style/Widget.Material.Notification.MessagingName"
+ android:layout_width="wrap_content"
+ />
<com.android.internal.widget.MessagingLinearLayout
android:id="@+id/group_message_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:spacing="2dp"
android:layout_weight="1"/>
- <com.android.internal.widget.ImageFloatingTextView
- android:id="@+id/message_name"
- style="@style/Widget.Material.Notification.MessagingName"
- android:layout_width="wrap_content"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
- android:paddingTop="2dp"
- />
</com.android.internal.widget.RemeasuringLinearLayout>
</com.android.internal.widget.MessagingGroup>
diff --git a/core/res/res/layout/notification_template_right_icon.xml b/core/res/res/layout/notification_template_right_icon.xml
index 8fb2887..0b97e45 100644
--- a/core/res/res/layout/notification_template_right_icon.xml
+++ b/core/res/res/layout/notification_template_right_icon.xml
@@ -19,7 +19,7 @@
android:id="@+id/right_icon_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="36dp"
+ android:layout_marginTop="@dimen/notification_content_margin_top"
android:layout_gravity="top|end">
<ImageView android:id="@+id/right_icon"
android:layout_width="@dimen/notification_right_icon_size"
@@ -32,8 +32,8 @@
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_gravity="top|end"
- android:layout_marginTop="28dp"
- android:layout_marginEnd="12dp"
+ android:layout_marginTop="27dp"
+ android:layout_marginEnd="16dp"
android:background="@drawable/notification_reply_background"
android:src="@drawable/ic_reply_notification"
android:scaleType="center"
diff --git a/core/res/res/drawable/messaging_message_background.xml b/core/res/res/layout/notification_template_smart_reply_container.xml
similarity index 63%
rename from core/res/res/drawable/messaging_message_background.xml
rename to core/res/res/layout/notification_template_smart_reply_container.xml
index 8a2096a..637241e 100644
--- a/core/res/res/drawable/messaging_message_background.xml
+++ b/core/res/res/layout/notification_template_smart_reply_container.xml
@@ -14,13 +14,11 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle"
- android:tint="#14000000">
- <corners android:radius="4dp" />
- <padding android:bottom="6dp"
- android:left="8dp"
- android:right="8dp"
- android:top="6dp" />
-</shape>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/smart_reply_container"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <!-- SmartReplyView will be added here. -->
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/raw/color_fade_frag.frag b/core/res/res/raw/color_fade_frag.frag
index a66a5a7..29975d5 100644
--- a/core/res/res/raw/color_fade_frag.frag
+++ b/core/res/res/raw/color_fade_frag.frag
@@ -3,40 +3,12 @@
precision mediump float;
uniform samplerExternalOES texUnit;
uniform float opacity;
-uniform float saturation;
uniform float gamma;
varying vec2 UV;
-vec3 rgb2hsl(vec3 rgb)
-{
- float e = 1.0e-7;
-
- vec4 p = rgb.g < rgb.b ? vec4(rgb.bg, -1.0, 2.0 / 3.0) : vec4(rgb.gb, 0.0, -1.0 / 3.0);
- vec4 q = rgb.r < p.x ? vec4(p.xyw, rgb.r) : vec4(rgb.r, p.yzx);
-
- float v = q.x;
- float c = v - min(q.w, q.y);
- float h = abs((q.w - q.y) / (6.0 * c + e) + q.z);
- float l = v - c * 0.5;
- float s = c / (1.0 - abs(2.0 * l - 1.0) + e);
- return clamp(vec3(h, s, l), 0.0, 1.0);
-}
-
-vec3 hsl2rgb(vec3 hsl)
-{
- vec3 h = vec3(hsl.x * 6.0);
- vec3 p = abs(h - vec3(3.0, 2.0, 4.0));
- vec3 q = 2.0 - p;
-
- vec3 rgb = clamp(vec3(p.x - 1.0, q.yz), 0.0, 1.0);
- float c = (1.0 - abs(2.0 * hsl.z - 1.0)) * hsl.y;
- return (rgb - vec3(0.5)) * c + hsl.z;
-}
-
void main()
{
vec4 color = texture2D(texUnit, UV);
- vec3 hsl = rgb2hsl(color.xyz);
- vec3 rgb = pow(hsl2rgb(vec3(hsl.x, hsl.y * saturation, hsl.z * opacity)), vec3(gamma));
+ vec3 rgb = pow(color.rgb * opacity, vec3(gamma));
gl_FragColor = vec4(rgb, 1.0);
}
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index e33cfc1..1bb3417 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Kon nie die kortpad teruglaai nie omdat die program nie rugsteun en teruglaai steun nie"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Kon nie teruglaai nie omdat programondertekening nie ooreenstem nie"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Kon nie kortpad teruglaai nie"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index e992266..ee287c3 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -217,6 +217,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"ድንገተኛ አደጋ"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"የሳንካ ሪፖርት"</string>
<string name="global_action_logout" msgid="935179188218826050">"ክፍለ-ጊዜን አብቃ"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"ቅጽበታዊ ገጽ እይታ"</string>
<string name="bugreport_title" msgid="2667494803742548533">"የሳንካ ሪፖርት ውሰድ"</string>
<string name="bugreport_message" msgid="398447048750350456">"ይሄ እንደ የኢሜይል መልዕክት አድርጎ የሚልከውን ስለመሣሪያዎ የአሁኑ ሁኔታ መረጃ ይሰበስባል። የሳንካ ሪፖርቱን ከመጀመር ጀምሮ እስኪላክ ድረስ ትንሽ ጊዜ ይወስዳል፤ እባክዎ ይታገሱ።"</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"መስተጋብራዊ ሪፖርት"</string>
@@ -1123,6 +1124,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi በራስ-ሰር ይበራል"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"ከፍተኛ ጥራት ያለው የተቀመጠ አውታረ መረብ አቅራቢያ ሲሆኑ"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"መልሰህ አታብራ"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi በራስ-ሰር በርቷል"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"ከተቀመጠ አውታረ መረብ አቅራቢያ ነዎት፦ <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"ወደ Wi-Fi አውታረ መረብ በመለያ ግባ"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"ወደ አውታረ መረብ በመለያ ይግቡ"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1807,4 +1810,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"መተግበሪያ ምትኬን እና ወደ ነበረበት መመለስን ሳለማይደግፍ አቋራጭ ወደ ነበረበት ሊመለስ አልቻለም"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"በመተግበሪያ ፊርማ አለመዛመድ አቋራጭን ወደነበረበት መመለስ አልተቻለም"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"አቋራጭን ወደ ነበረበት መመለስ አልተቻለም"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"አቋራጭ ተሰናክሏል"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"አራግፍ"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"የሆነው ሆኖ አስጀምር"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"ጎጂ መተግበሪያ ይራገፍ?"</string>
</resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 2002084..e929bf8 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -51,7 +51,7 @@
<string name="needPuk2" msgid="4526033371987193070">"اكتب PUK2 لإلغاء تأمين شريحة SIM."</string>
<string name="enablePin" msgid="209412020907207950">"محاولة غير ناجحة، مكّن قفل SIM/RUIM."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1251012001539225582">
- <item quantity="zero">لم يتبق لديك أية محاولات (<xliff:g id="NUMBER_1">%d</xliff:g>) يتم بعدها قفل شريحة SIM.</item>
+ <item quantity="zero">لم يتبق لديك أي محاولات (<xliff:g id="NUMBER_1">%d</xliff:g>) يتم بعدها قفل شريحة SIM.</item>
<item quantity="two">يتبقى لديك محاولتان (<xliff:g id="NUMBER_1">%d</xliff:g>) يتم بعدهما قفل شريحة SIM.</item>
<item quantity="few">يتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولات يتم بعدها قفل شريحة SIM.</item>
<item quantity="many">يتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولة يتم بعدها قفل شريحة SIM.</item>
@@ -448,9 +448,9 @@
<string name="permdesc_setTimeZone" product="tv" msgid="888864653946175955">"يتيح للتطبيق تغيير المنطقة الزمنية للتلفزيون."</string>
<string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"للسماح للتطبيق بتغيير المنطقة الزمنية للهاتف."</string>
<string name="permlab_getAccounts" msgid="1086795467760122114">"البحث عن حسابات على الجهاز"</string>
- <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"للسماح للتطبيق بالحصول على قائمة بالحسابات التي يعرفها الجهاز اللوحي. وقد يتضمن ذلك أية حسابات تم إنشاؤها بواسطة التطبيقات التي ثبتها."</string>
- <string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"يتيح للتطبيق الحصول على قائمة بالحسابات المعروفة في التلفزيون. وقد يتضمن هذا أية حسابات أنشأتها التطبيقات التي ثبتها."</string>
- <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"للسماح للتطبيق بالحصول على قائمة بالحسابات التي يعرفها الهاتف. وقد يتضمن ذلك أية حسابات تم إنشاؤها بواسطة التطبيقات التي ثبتها."</string>
+ <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"للسماح للتطبيق بالحصول على قائمة بالحسابات التي يعرفها الجهاز اللوحي. وقد يتضمن ذلك أي حسابات تم إنشاؤها بواسطة التطبيقات التي ثبتها."</string>
+ <string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"يتيح للتطبيق الحصول على قائمة بالحسابات المعروفة في التلفزيون. وقد يتضمن هذا أي حسابات أنشأتها التطبيقات التي ثبتها."</string>
+ <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"للسماح للتطبيق بالحصول على قائمة بالحسابات التي يعرفها الهاتف. وقد يتضمن ذلك أي حسابات تم إنشاؤها بواسطة التطبيقات التي ثبتها."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"عرض اتصالات الشبكة"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"للسماح للتطبيق بعرض معلومات حول اتصالات الشبكة كعرض معلومات عن الشبكات المتوفرة والشبكات المتصلة."</string>
<string name="permlab_createNetworkSockets" msgid="7934516631384168107">"حق الوصول الكامل إلى الشبكة"</string>
@@ -472,7 +472,7 @@
<string name="permdesc_bluetoothAdmin" product="tv" msgid="3373125682645601429">"يتيح للتطبيق تهيئة تلفزيون بلوتوث المحلي، واكتشاف الأجهزة البعيدة وإقرانها."</string>
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"للسماح للتطبيق بتهيئة هاتف البلوتوث المحلي، واكتشاف أجهزة التحكم عن بعد والاقتران بها."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"الاتصال بـشبكة WiMAX وقطع الاتصال بها"</string>
- <string name="permdesc_accessWimaxState" msgid="6360102877261978887">"للسماح للتطبيق بتحديد ما إذا تم تمكين WiMAX وتحديد معلومات حول أية شبكات WiMAX متصلة."</string>
+ <string name="permdesc_accessWimaxState" msgid="6360102877261978887">"للسماح للتطبيق بتحديد ما إذا تم تمكين WiMAX وتحديد معلومات حول أي شبكات WiMAX متصلة."</string>
<string name="permlab_changeWimaxState" msgid="340465839241528618">"تغيير حالة WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"للسماح للتطبيق بتوصيل الجهاز اللوحي بشبكات WiMAX وقطع اتصاله بها."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"يتيح للتطبيق توصيل التلفزيون أو إلغاء توصيله بشبكات WiMAX."</string>
@@ -817,7 +817,7 @@
<string name="granularity_label_line" msgid="5764267235026120888">"سطر"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"تعذّر اختبار المصنع"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"إجراء FACTORY_TEST غير متاح سوى للحزم المثبتة في /system/app."</string>
- <string name="factorytest_no_action" msgid="872991874799998561">"لم يتم العثور على أية حزمة توفر إجراء FACTORY_TEST."</string>
+ <string name="factorytest_no_action" msgid="872991874799998561">"لم يتم العثور على أي حزمة توفر إجراء FACTORY_TEST."</string>
<string name="factorytest_reboot" msgid="6320168203050791643">"إعادة تشغيل"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"تعرض الصفحة في \"<xliff:g id="TITLE">%s</xliff:g>\":"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"جافا سكريبت"</string>
@@ -1186,7 +1186,7 @@
<string name="ringtone_picker_title_notification" msgid="4837740874822788802">"أصوات الإشعار"</string>
<string name="ringtone_unknown" msgid="3914515995813061520">"غير معروف"</string>
<plurals name="wifi_available" formatted="false" msgid="7900333017752027322">
- <item quantity="zero">لا تتوفر أية شبكات Wi-Fi</item>
+ <item quantity="zero">لا تتوفر أي شبكات Wi-Fi</item>
<item quantity="two">تتوفر شبكتا Wi-Fi</item>
<item quantity="few">تتوفر شبكات Wi-Fi</item>
<item quantity="many">تتوفر شبكات Wi-Fi</item>
@@ -1194,7 +1194,7 @@
<item quantity="one">تتوفر شبكة Wi-Fi واحدة</item>
</plurals>
<plurals name="wifi_available_detailed" formatted="false" msgid="1140699367193975606">
- <item quantity="zero">لا تتوفر أية شبكات Wi-Fi مفتوحة</item>
+ <item quantity="zero">لا تتوفر أي شبكات Wi-Fi مفتوحة</item>
<item quantity="two">تتوفر شبكتا Wi-Fi مفتوحتان</item>
<item quantity="few">تتوفر شبكات Wi-Fi مفتوحة</item>
<item quantity="many">تتوفر شبكات Wi-Fi مفتوحة</item>
@@ -1413,7 +1413,7 @@
<string name="back_button_label" msgid="2300470004503343439">"رجوع"</string>
<string name="next_button_label" msgid="1080555104677992408">"التالي"</string>
<string name="skip_button_label" msgid="1275362299471631819">"تخطي"</string>
- <string name="no_matches" msgid="8129421908915840737">"ليس هناك أية مطابقات"</string>
+ <string name="no_matches" msgid="8129421908915840737">"ليس هناك أي مطابقات"</string>
<string name="find_on_page" msgid="1946799233822820384">"بحث في الصفحة"</string>
<plurals name="matches_found" formatted="false" msgid="1210884353962081884">
<item quantity="zero"><xliff:g id="INDEX">%d</xliff:g> من <xliff:g id="TOTAL">%d</xliff:g></item>
@@ -1947,4 +1947,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"تعذّرت استعادة الاختصار لأن التطبيق لا يوفِّر إمكانية النسخ الاحتياطي والاستعادة"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"تعذّرت استعادة الاختصار بسبب عدم تطابق توقيع التطبيق"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"تعذّرت استعادة الاختصار"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index a05d49f..8069d6c 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Qısayolu bərpa etmək mümkün olmadı, çünki tətbiq yedəkləməni və bərpa etməyi dəstəkləmir"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Tətbiqin imza uyğunsuzluğu səbəbilə qısayolu bərpa etmək mümkün olmadı"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Qısayolu bərpa etmək mümkün olmadı"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index ce220db..bb5a404 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1842,4 +1842,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Vraćanje prečice nije uspelo jer aplikacija ne podržava pravljenje rezervne kopije i vraćanje"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Vraćanje prečice nije uspelo jer se potpisi aplikacija ne podudaraju"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Vraćanje prečice nije uspelo"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index f636f48..b5e28b3 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1877,4 +1877,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Не атрымалася аднавіць ярлык, бо праграма не падтрымлівае рэзервовае капіраванне і аднаўленне"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Не атрымалася аднавіць ярлык з-за несупадзення подпісаў праграм"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Не атрымалася аднавіць ярлык"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 7a37939..e069c4e 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Прекият път не можа да бъде възстановен, защото приложението не поддържа създаването на резервно копие и възстановяването"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Прекият път не можа да бъде възстановен поради несъответствие в подписа на приложението"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Прекият път не можа да бъде възстановен"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index b88d2c83..9b1f6c3 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1808,4 +1808,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"শর্টকাট ফিরিয়ে আনা যায়নি কারণ অ্যাপটিতে \'ব্যাক-আপ এবং রিস্টোর\' করার সুবিধা নেই"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"শর্টকাট ফিরিয়ে আনা যায়নি কারণ অ্যাপের সিগ্নেচারটি মিল হচ্ছে না"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"শর্টকাট ফিরিয়ে আনা যায়নি"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index e19a974..c79566b 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1844,4 +1844,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Prečica nije uspješno vraćena jer aplikacija ne podržava izradu sigurnosne kopije i vraćanje"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Prečica nije uspješno vraćena zbog nepodudaranja potpisa aplikacije"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Prečica nije uspješno vraćena"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 7e109ac..ea09008 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"No s\'ha pogut restaurar la drecera perquè l\'aplicació no permet la còpia de seguretat ni la restauració"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"No s\'ha pogut restaurar la drecera perquè la signatura de l\'aplicació no coincideix"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"No s\'ha pogut restaurar la drecera"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 8b0e46e..6b5f1c8 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -221,6 +221,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Stav nouze"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Hlášení chyb"</string>
<string name="global_action_logout" msgid="935179188218826050">"Ukončit relaci"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Snímek obrazovky"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Vytvořit chybové hlášení"</string>
<string name="bugreport_message" msgid="398447048750350456">"Shromažďuje informace o aktuálním stavu zařízení. Tyto informace je následně možné poslat v e-mailové zprávě, chvíli však potrvá, než bude hlášení o chybě připraveno k odeslání. Buďte prosím trpěliví."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktivní přehled"</string>
@@ -1167,6 +1168,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi se zapne automaticky"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Když budete v dosahu kvalitní uložené sítě"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Znovu nezapínat"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Automaticky se zapnulo připojení Wi-Fi"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Jste v dosahu uložené sítě: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Přihlásit se k síti Wi-Fi"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Přihlásit se k síti"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1877,4 +1880,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Zkratku nelze obnovit, protože aplikace nepodporuje zálohování a obnovu"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Zkratku nelze obnovit, protože se neshoduje podpis aplikace"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Zkratku nelze obnovit"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Zkratka nefunguje"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Odinstalovat"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Přesto spustit"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"Odinstalovat škodlivou aplikaci?"</string>
</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 0be9d20..90b7e5e 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Genvejen kunne ikke gendannes, da appen ikke understøtter backup og gendannelse"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Genvejen kunne ikke gendannes på grund af uoverensstemmelse i appsignatur"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Genvejen kunne ikke gendannes"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 367949c..b94cc90 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -217,6 +217,8 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Notfall"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Fehlerbericht"</string>
<string name="global_action_logout" msgid="935179188218826050">"Sitzung beenden"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="bugreport_title" msgid="2667494803742548533">"Fehlerbericht abrufen"</string>
<string name="bugreport_message" msgid="398447048750350456">"Bei diesem Fehlerbericht werden Daten zum aktuellen Status deines Geräts erfasst und als E-Mail versandt. Vom Start des Berichts bis zu seinem Versand kann es eine Weile dauern. Bitte habe etwas Geduld."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktiver Bericht"</string>
@@ -379,7 +381,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Ermöglicht der App, die Anrufliste deines Tablets zu ändern, einschließlich der Daten über ein- und ausgehende Anrufe. Schädliche Apps können so deine Anrufliste löschen oder ändern."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Ermöglicht der App, die Anrufliste deines Fernsehers zu ändern, einschließlich der Daten über ein- und ausgehende Anrufe. Schädliche Apps können so deine Anrufliste löschen oder ändern."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Ermöglicht der App, die Anrufliste deines Telefons zu ändern, einschließlich der Daten über ein- und ausgehende Anrufe. Schädliche Apps können so deine Anrufliste löschen oder ändern."</string>
- <string name="permlab_bodySensors" msgid="4683341291818520277">"Auf Körpersensoren wie z. B. Herzfrequenzmesser zugreifen"</string>
+ <string name="permlab_bodySensors" msgid="4683341291818520277">"Auf Körpersensoren wie z. B. Pulsmesser zugreifen"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Ermöglicht der App, auf Daten von Sensoren zuzugreifen, die deine körperliche Verfassung überwachen, beispielsweise deinen Puls"</string>
<string name="permlab_readCalendar" msgid="6716116972752441641">"Kalendertermine und Details lesen"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4993979255403945892">"Diese App kann alle auf deinem Tablet gespeicherten Kalendertermine lesen und deine Kalenderdaten teilen oder speichern."</string>
@@ -1123,6 +1125,10 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"WLAN wird automatisch aktiviert"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Wenn du in der Nähe eines sicheren gespeicherten Netzwerks bist"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Nicht wieder aktivieren"</string>
+ <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) -->
+ <skip />
+ <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) -->
+ <skip />
<string name="wifi_available_sign_in" msgid="9157196203958866662">"In WLAN anmelden"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Im Netzwerk anmelden"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1807,4 +1813,11 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Verknüpfung konnte nicht wiederhergestellt werden, weil die App keine Sicherung und keine Wiederherstellung unterstützt"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Verknüpfung konnte nicht wiederhergestellt werden, weil die App-Signatur nicht übereinstimmt"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Verknüpfung konnte nicht wiederhergestellt werden"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Verknüpfung ist deaktiviert"</string>
+ <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_title (2229996292333310435) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 05c51cf..9aa3eb5 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Δεν ήταν δυνατή η επαναφορά της συντόμευσης, επειδή η εφαρμογή δεν υποστηρίζει τη δημιουργία αντιγράφων ασφαλείας και την επαναφορά"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Δεν ήταν δυνατή η επαναφορά της συντόμευσης, λόγω αναντιστοιχίας της υπογραφής εφαρμογής"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Δεν ήταν δυνατή η επαναφορά της συντόμευσης"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index ed97563..2910067 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -217,6 +217,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Emergency"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Bug report"</string>
<string name="global_action_logout" msgid="935179188218826050">"End session"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Take bug report"</string>
<string name="bugreport_message" msgid="398447048750350456">"This will collect information about your current device state, to send as an email message. It will take a little time from starting the bug report until it is ready to be sent. Please be patient."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interactive report"</string>
@@ -1123,6 +1124,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high‑quality saved network"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi turned on automatically"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"You\'re near a saved network: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Sign in to a Wi-Fi network"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Sign in to network"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1807,4 +1810,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Couldn’t restore shortcut because app doesn’t support backup and restore"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Couldn’t restore shortcut because of app signature mismatch"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Couldn’t restore shortcut"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Shortcut is disabled"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Uninstall"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Launch anyway"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"Uninstall harmful app?"</string>
</resources>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index ed97563..2910067 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -217,6 +217,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Emergency"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Bug report"</string>
<string name="global_action_logout" msgid="935179188218826050">"End session"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Take bug report"</string>
<string name="bugreport_message" msgid="398447048750350456">"This will collect information about your current device state, to send as an email message. It will take a little time from starting the bug report until it is ready to be sent. Please be patient."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interactive report"</string>
@@ -1123,6 +1124,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high‑quality saved network"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi turned on automatically"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"You\'re near a saved network: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Sign in to a Wi-Fi network"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Sign in to network"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1807,4 +1810,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Couldn’t restore shortcut because app doesn’t support backup and restore"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Couldn’t restore shortcut because of app signature mismatch"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Couldn’t restore shortcut"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Shortcut is disabled"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Uninstall"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Launch anyway"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"Uninstall harmful app?"</string>
</resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index ed97563..2910067 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -217,6 +217,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Emergency"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Bug report"</string>
<string name="global_action_logout" msgid="935179188218826050">"End session"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Take bug report"</string>
<string name="bugreport_message" msgid="398447048750350456">"This will collect information about your current device state, to send as an email message. It will take a little time from starting the bug report until it is ready to be sent. Please be patient."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interactive report"</string>
@@ -1123,6 +1124,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high‑quality saved network"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi turned on automatically"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"You\'re near a saved network: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Sign in to a Wi-Fi network"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Sign in to network"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1807,4 +1810,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Couldn’t restore shortcut because app doesn’t support backup and restore"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Couldn’t restore shortcut because of app signature mismatch"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Couldn’t restore shortcut"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Shortcut is disabled"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Uninstall"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Launch anyway"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"Uninstall harmful app?"</string>
</resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index ed97563..2910067 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -217,6 +217,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Emergency"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Bug report"</string>
<string name="global_action_logout" msgid="935179188218826050">"End session"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Take bug report"</string>
<string name="bugreport_message" msgid="398447048750350456">"This will collect information about your current device state, to send as an email message. It will take a little time from starting the bug report until it is ready to be sent. Please be patient."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interactive report"</string>
@@ -1123,6 +1124,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high‑quality saved network"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi turned on automatically"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"You\'re near a saved network: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Sign in to a Wi-Fi network"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Sign in to network"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1807,4 +1810,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Couldn’t restore shortcut because app doesn’t support backup and restore"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Couldn’t restore shortcut because of app signature mismatch"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Couldn’t restore shortcut"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Shortcut is disabled"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Uninstall"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Launch anyway"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"Uninstall harmful app?"</string>
</resources>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 2833630..5f7f19c 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -217,6 +217,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Emergency"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Bug report"</string>
<string name="global_action_logout" msgid="935179188218826050">"End session"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Take bug report"</string>
<string name="bugreport_message" msgid="398447048750350456">"This will collect information about your current device state, to send as an e-mail message. It will take a little time from starting the bug report until it is ready to be sent; please be patient."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interactive report"</string>
@@ -1123,6 +1124,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high quality saved network"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi turned on automatically"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"You\'re near a saved network: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Sign in to Wi-Fi network"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Sign in to network"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1807,4 +1810,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Couldn’t restore shortcut because app doesn’t support backup and restore"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Couldn’t restore shortcut because of app signature mismatch"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Couldn’t restore shortcut"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Shortcut is disabled"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Uninstall"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Launch anyway"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"Uninstall harmful app?"</string>
</resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 3efbad9..1fe3c9a 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Error al restablecer el acceso directo porque la app no admite la opción de copia de seguridad y restauración"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Error al restablecer el acceso directo por falta de coincidencia con la firma de apps"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Error al restablecer el acceso directo"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 5a0a118..cf25ed3 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -453,7 +453,7 @@
<string name="permdesc_changeWifiState" msgid="7137950297386127533">"Permite que la aplicación se conecte a puntos de acceso Wi-Fi y se desconecte de ellos y que realice cambios en la configuración de redes Wi-Fi del dispositivo."</string>
<string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"permitir recepción multidifusión Wi-Fi"</string>
<string name="permdesc_changeWifiMulticastState" product="tablet" msgid="7969774021256336548">"Permite que la aplicación reciba paquetes enviados a todos los dispositivos de una red Wi-Fi que utilicen direcciones de multidifusión, no solo al tablet. Utiliza más batería que el modo de no multidifusión."</string>
- <string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"Permite que la aplicación reciba paquetes enviados a todos los dispositivos a través de una red Wi-Fi mediante direcciones de multidifusión, no solo la TV. Consume más energía que el modo sin multidifusión."</string>
+ <string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"Permite que la aplicación reciba paquetes enviados a todos los dispositivos a través de una red Wi-Fi mediante direcciones de multidifusión, no solo la TV. Consume más batería que el modo sin multidifusión."</string>
<string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"Permite que la aplicación reciba paquetes enviados a todos los dispositivos de una red Wi-Fi que utilicen direcciones de multidifusión, no solo al teléfono. Utiliza más batería que el modo de no multidifusión."</string>
<string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"acceder a los ajustes de Bluetooth"</string>
<string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"Permite que la aplicación configure el tablet Bluetooth local y que detecte dispositivos remotos y se vincule con ellos."</string>
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"No se ha podido restaurar el acceso directo porque la aplicación no es compatible con la función de copia de seguridad y restauración"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"No se ha podido restaurar el acceso directo porque la firma de la aplicación no coincide"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"No se ha podido restaurar el acceso directo"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 4b2d13b..c0821c7 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Otseteed ei õnnestunud taastada, kuna rakendus ei toeta varundamist ega taastamist"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Otseteed ei õnnestunud taastada, kuna rakenduse allkiri ei ühti"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Otseteed ei õnnestunud taastada"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index f468b5d..960dfd8 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -86,7 +86,7 @@
<string name="NetworkPreferenceSwitchSummary" msgid="7056776609127756440">"Seinalea hobea izan dadin, aldatu sare mota Ezarpenak > Sareak eta Internet > Sare mugikorrak > Sare mota hobetsia atalean."</string>
<string name="EmergencyCallWarningTitle" msgid="4790413876281901612">"Wi‑Fi bidezko deiak aktibo daude"</string>
<string name="EmergencyCallWarningSummary" msgid="8973232888021643293">"Sare mugikorrera konektatuta egon behar da larrialdi-deiak egin ahal izateko."</string>
- <string name="notification_channel_network_alert" msgid="4427736684338074967">"Abisuak"</string>
+ <string name="notification_channel_network_alert" msgid="4427736684338074967">"Alertak"</string>
<string name="notification_channel_call_forward" msgid="2419697808481833249">"Dei-desbideratzea"</string>
<string name="notification_channel_emergency_callback" msgid="6686166232265733921">"Larrialdi-deiak soilik jasotzeko modua"</string>
<string name="notification_channel_mobile_data_status" msgid="4575131690860945836">"Datu mugikorren egoera"</string>
@@ -217,6 +217,8 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Larrialdi-deiak"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Akatsen txostena"</string>
<string name="global_action_logout" msgid="935179188218826050">"Amaitu saioa"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="bugreport_title" msgid="2667494803742548533">"Sortu akatsen txostena"</string>
<string name="bugreport_message" msgid="398447048750350456">"Gailuaren uneko egoerari buruzko informazioa bilduko da, mezu elektroniko gisa bidaltzeko. Minutu batzuk igaroko dira akatsen txostena sortzen hasten denetik bidaltzeko prest egon arte. Itxaron, mesedez."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Txosten dinamikoa"</string>
@@ -251,7 +253,7 @@
<string name="notification_channel_network_available" msgid="4531717914138179517">"Sare bat erabilgarri dago"</string>
<string name="notification_channel_vpn" msgid="8330103431055860618">"VPN egoera"</string>
<string name="notification_channel_device_admin" msgid="1568154104368069249">"Gailuen administrazioa"</string>
- <string name="notification_channel_alerts" msgid="4496839309318519037">"Abisuak"</string>
+ <string name="notification_channel_alerts" msgid="4496839309318519037">"Alertak"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Saltzaileentzako demoa"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"USB konexioa"</string>
<string name="notification_channel_heavy_weight_app" msgid="6218742927792852607">"Aplikazio bat abian da"</string>
@@ -294,7 +296,7 @@
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Eskuratu leihoko edukia"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Arakatu irekita daukazun leihoko edukia."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Aktibatu \"Arakatu ukituta\""</string>
- <string name="capability_desc_canRequestTouchExploration" msgid="7543249041581408313">"Sakatutako elementuak ozen esango dira eta pantaila keinu bidez arakatu ahal izango da."</string>
+ <string name="capability_desc_canRequestTouchExploration" msgid="7543249041581408313">"Sakatutako elementuak ozen irakurriko dira eta pantaila keinu bidez arakatu ahal izango da."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Behatu idazten duzun testua"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Ez da salbuespenik egiten datu pertsonalekin, hala nola, kreditu-txartelen zenbakiekin eta pasahitzekin."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kontrolatu pantailaren zoom-maila"</string>
@@ -1123,6 +1125,10 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi konexioa automatikoki aktibatuko da"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Gordeta daukazun kalitate handiko sare batetik gertu zaudenean"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ez aktibatu berriro"</string>
+ <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) -->
+ <skip />
+ <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) -->
+ <skip />
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Hasi saioa Wi-Fi sarean"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Hasi saioa sarean"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1808,4 +1814,11 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Ezin izan da leheneratu lasterbidea aplikazioak ez duelako onartzen babeskopiak egiteko eta leheneratzeko aukera"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Ezin izan da leheneratu lasterbidea aplikazioaren sinadurak ez datozelako bat"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Ezin izan da leheneratu lasterbidea"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Desgaituta dago lasterbidea"</string>
+ <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_title (2229996292333310435) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 8061962..0458336 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"نمیتوان میانبر را بازیابی کرد زیرا برنامه از پشتیبانگیری و بازیابی پشتیبانی نمیکند"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"بهعلت عدم تطابق امضای برنامه نمیتوان میانبر را بازیابی کرد"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"نمیتوان میانبر را بازیابی کرد"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 4385466..d6d1315 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Pikakuvakkeen palautus epäonnistui, koska sovellus ei tue varmuuskopiointia eikä palauttamista."</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Pikakuvakkeen palautus epäonnistui sovelluksen allekirjoituksen yhteensopimattomuuden vuoksi."</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Pikakuvakkeen palautus epäonnistui."</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 49a3640..aa6608a 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -217,6 +217,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Urgence"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Rapport de bogue"</string>
<string name="global_action_logout" msgid="935179188218826050">"Fermer la session"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Capture d\'écran"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Créer un rapport de bogue"</string>
<string name="bugreport_message" msgid="398447048750350456">"Cela permet de recueillir des informations concernant l\'état actuel de votre appareil. Ces informations sont ensuite envoyées sous forme de courriel. Merci de patienter pendant la préparation du rapport de bogue. Cette opération peut prendre quelques instants."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Rapport interactif"</string>
@@ -1123,6 +1124,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Le Wi-Fi s\'activera automatiquement"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Lorsque vous êtes près d\'un réseau enregistré de haute qualité"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ne pas réactiver"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi-Fi activé automatiquement"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Vous êtes à proximité d\'un réseau enregistré : <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Connectez-vous au réseau Wi-Fi"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Connectez-vous au réseau"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1807,4 +1810,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Impossible de restaurer le raccourci, car l\'application ne prend pas en charge la sauvegarde et la restauration"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Impossible de restaurer le raccourci en raison d\'une erreur de correspondance des signature d\'applications"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Impossible de restaurer le raccourci"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Le raccourci est désactivé"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Désinstaller"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Lancer quand même"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"Désinstaller l\'application nuisible?"</string>
</resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 5564d8b..06a5684 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -217,6 +217,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Urgences"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Rapport de bug"</string>
<string name="global_action_logout" msgid="935179188218826050">"Fermer la session"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Capture d\'écran"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Créer un rapport de bug"</string>
<string name="bugreport_message" msgid="398447048750350456">"Cela permet de recueillir des informations concernant l\'état actuel de votre appareil. Ces informations sont ensuite envoyées sous forme d\'e-mail. Merci de patienter pendant la préparation du rapport de bug. Cette opération peut prendre quelques instants."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Rapport interactif"</string>
@@ -1123,6 +1124,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Le Wi-Fi sera activé automatiquement"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Lorsque vous êtes à proximité d\'un réseau enregistré de haute qualité"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ne pas réactiver"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi-Fi activé automatiquement"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Vous vous trouvez à proximité d\'un réseau enregistré : <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Connectez-vous au réseau Wi-Fi"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Se connecter au réseau"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1807,4 +1810,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Le raccourci ne peut pas être restauré car l\'application n\'accepte pas la sauvegarde et la restauration"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Le raccourci ne peut pas être restauré car la signature de l\'application est différente"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Impossible de restaurer le raccourci"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Le raccourci est désactivé"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Désinstaller"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Lancer quand même"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"Désinstaller l\'application malveillante ?"</string>
</resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 40dff7e..a53fac7 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -133,7 +133,7 @@
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Desactivado"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wifi preferida"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Datos móbiles preferidos"</string>
- <string name="wfc_mode_wifi_only_summary" msgid="2379919155237869320">"Só wifi"</string>
+ <string name="wfc_mode_wifi_only_summary" msgid="2379919155237869320">"Só por wifi"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: non desviada"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> tras <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string>
@@ -1808,4 +1808,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Non se puido restaurar o atallo porque a aplicación non é compatible coa restauración e a copia de seguranza"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Non se puido restaurar o atallo porque a sinatura da aplicación non coincide"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Non se puido restaurar o atallo"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 7a4c1b2..2af95ce 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -217,6 +217,8 @@
<string name="global_action_emergency" msgid="7112311161137421166">"કટોકટી"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"બગ રિપોર્ટ"</string>
<string name="global_action_logout" msgid="935179188218826050">"સત્ર સમાપ્ત કરો"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="bugreport_title" msgid="2667494803742548533">"બગ રિપોર્ટ લો"</string>
<string name="bugreport_message" msgid="398447048750350456">"આ, એક ઇ-મેઇલ સંદેશ તરીકે મોકલવા માટે, તમારા વર્તમાન ઉપકરણ સ્થિતિ વિશેની માહિતી એકત્રિત કરશે. એક બગ રિપોર્ટ પ્રારંભ કરીને તે મોકલવા માટે તૈયાર ન થઈ જાય ત્યાં સુધી તેમાં થોડો સમય લાગશે; કૃપા કરીને ધીરજ રાખો."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ક્રિયાપ્રતિક્રિયાત્મક રિપોર્ટ"</string>
@@ -1123,6 +1125,10 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"વાઇ-ફાઇ આપમેળે ચાલુ થઈ જશે"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"જ્યારે તમે એક ઉચ્ચ ક્વૉલિટીવાળા સાચવેલ નેટવર્કની નજીક હોવ"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"પાછું ચાલુ કરશો નહીં"</string>
+ <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) -->
+ <skip />
+ <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) -->
+ <skip />
<string name="wifi_available_sign_in" msgid="9157196203958866662">"વાઇ-ફાઇ નેટવર્ક પર સાઇન ઇન કરો"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"નેટવર્ક પર સાઇન ઇન કરો"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1808,4 +1814,11 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"શૉર્ટકટ ફરી મેળવી શકાયો નથી કારણ કે ઍપ બૅકઅપ અને ફરી મેળવવાનું સમર્થન કરતી નથી"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"શૉર્ટકટ ફરી મેળવી શકાયો નથી કારણ કે ઍપમાં છે તે સહી મેળ ખાતી નથી"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"શૉર્ટકટ પાછો મેળવી શકાયો નથી"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"શૉર્ટકટને બંધ કરવામાં આવ્યો છે"</string>
+ <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_title (2229996292333310435) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index adeba4e..251ec84 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -217,6 +217,8 @@
<string name="global_action_emergency" msgid="7112311161137421166">"आपातकाल"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"गड़बड़ी की रिपोर्ट"</string>
<string name="global_action_logout" msgid="935179188218826050">"सत्र खत्म करें"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="bugreport_title" msgid="2667494803742548533">"गड़बड़ी की रिपोर्ट लें"</string>
<string name="bugreport_message" msgid="398447048750350456">"इससे ईमेल भेजने के लिए, आपके डिवाइस की मौजूदा स्थिति से जुड़ी जानकारी इकट्ठा की जाएगी. गड़बड़ी की रिपोर्ट बनना शुरू होने से लेकर भेजने के लिए तैयार होने तक कुछ समय लगेगा; कृपया इंतज़ार करें."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"सहभागी रिपोर्ट"</string>
@@ -1123,6 +1125,10 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"वाई-फ़ाई अपने आप चालू हो जाएगा"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"जब आप किसी अच्छी क्वालिटी वाले सेव किए गए नेटवर्क के पास हों"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"वापस चालू न करें"</string>
+ <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) -->
+ <skip />
+ <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) -->
+ <skip />
<string name="wifi_available_sign_in" msgid="9157196203958866662">"वाई-फ़ाई नेटवर्क में साइन इन करें"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"नेटवर्क में साइन इन करें"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1807,4 +1813,11 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"शॉर्टकट बहाल नहीं किया जा सका क्योंकि इस ऐप में बैकअप लेने और उसे बहाल करने की सुविधा नहीं है"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"ऐप सिग्नेचर अलग होने के कारण शॉर्टकट बहाल नहीं किया जा सका"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"शॉर्टकट बहाल नहीं किया जा सका"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"शॉर्टकट बंद है"</string>
+ <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_title (2229996292333310435) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 6883769..bbe7ca9 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1842,4 +1842,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Vraćanje prečaca nije uspjelo jer aplikacija ne podržava sigurnosno kopiranje i vraćanje"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Vraćanje prečaca nije uspjelo zbog nepodudaranja potpisa aplikacije"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Vraćanje prečaca nije uspjelo"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 2545283..2dbe49d 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Nem sikerült visszaállítani a parancsikont, mert az alkalmazás nem támogatja a biztonsági mentést és visszaállítást"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Nem sikerült visszaállítani a parancsikont, mert az alkalmazás-aláírás nem egyezik"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Nem sikerült visszaállítani a parancsikont"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index d29c357..1218b48 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Չհաջողվեց վերականգնել դյուրանցումը, քանի որ հավելվածում չի աջակցվում պահուստավորման և վերականգնման գործառույթը"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Չհաջողվեց վերականգնել դյուրանցումը, քանի որ հավելվածների ստորագրությունները տարբեր են"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Չհաջողվեց վերականգնել դյուրանցումը"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index d5d8d99..e3df0c8 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -217,6 +217,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Darurat"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Laporan bug"</string>
<string name="global_action_logout" msgid="935179188218826050">"Akhiri sesi"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Ambil laporan bug"</string>
<string name="bugreport_message" msgid="398447048750350456">"Ini akan mengumpulkan informasi status perangkat Anda saat ini, untuk dikirimkan sebagai pesan email. Harap bersabar, mungkin perlu waktu untuk memulai laporan bug hingga siap dikirim."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Laporan interaktif"</string>
@@ -1123,6 +1124,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi akan aktif otomatis"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Saat berada di dekat jaringan berkualitas tinggi yang tersimpan"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Jangan aktifkan kembali"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi diaktifkan otomatis"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Anda berada di dekat jaringan yang tersimpan: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Masuk ke jaringan Wi-Fi"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Masuk ke jaringan"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1807,4 +1810,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Tidak dapat memulihkan pintasan karena aplikasi tidak mendukung backup dan pulihkan"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Tidak dapat memulihkan pintasan karena tanda tangan aplikasi tidak cocok"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Tidak dapat memulihkan pintasan."</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Pintasan dinonaktifkan"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Uninstal"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Tetap luncurkan"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"Uninstal aplikasi berbahaya?"</string>
</resources>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index b65da75..40bf34c 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1808,4 +1808,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Ekki var hægt að endurheimta flýtileið vegna þess að forritið styður ekki öryggisafritun og endurheimt"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Ekki var hægt að endurheimta flýtileið vegna þess að undirskriftir forrita passa ekki saman"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Ekki var hægt að endurheimta flýtileið"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 0f36301..c1ef402 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -217,6 +217,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Emergenza"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Segnalazione di bug"</string>
<string name="global_action_logout" msgid="935179188218826050">"Termina sessione"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Apri segnalazione bug"</string>
<string name="bugreport_message" msgid="398447048750350456">"Verranno raccolte informazioni sullo stato corrente del dispositivo che saranno inviate sotto forma di messaggio email. Passerà un po\' di tempo prima che la segnalazione di bug aperta sia pronta per essere inviata; ti preghiamo di avere pazienza."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Rapporto interattivo"</string>
@@ -1123,6 +1124,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Il Wi‑Fi verrà attivato automaticamente"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Quando ti trovi nell\'area di una rete salvata di alta qualità"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Non riattivare"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi attivato automaticamente"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Ti trovi nell\'area di copertura di una rete salvata: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Accedi a rete Wi-Fi"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Accedi alla rete"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1807,4 +1810,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Impossibile ripristinare la scorciatoia perché l\'app non supporta il backup e il ripristino"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Impossibile ripristinare la scorciatoia perché la firma dell\'app non corrisponde"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Impossibile ripristinare la scorciatoia"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"La scorciatoia è disattivata"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Disinstalla"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Avvia comunque"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"Disinstallare l\'app dannosa?"</string>
</resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index a152840..7c0ce3a 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1877,4 +1877,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"לא ניתן היה לשחזר את קיצור הדרך מפני שהאפליקציה אינה תומכת בגיבוי ובשחזור"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"לא ניתן היה לשחזר את קיצור הדרך עקב חוסר התאמה בחתימה על האפליקציות"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"לא ניתן היה לשחזר את קיצור הדרך"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 2f11776..3b124ff 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -217,6 +217,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"緊急通報"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"バグレポート"</string>
<string name="global_action_logout" msgid="935179188218826050">"セッションを終了"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"スクリーンショット"</string>
<string name="bugreport_title" msgid="2667494803742548533">"バグレポートを取得"</string>
<string name="bugreport_message" msgid="398447048750350456">"現在の端末の状態に関する情報が収集され、その内容がメールで送信されます。バグレポートが開始してから送信可能な状態となるまでには多少の時間がかかりますのでご了承ください。"</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"対話型レポート"</string>
@@ -1123,6 +1124,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi は自動的にオンになります"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"高品質の保存済みネットワークの検出時"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"再度オンにしない"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi が自動的に ON になりました"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"保存済みネットワーク「<xliff:g id="NETWORK_SSID">%1$s</xliff:g>」の近くにいます"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Wi-Fiネットワークにログイン"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"ネットワークにログインしてください"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1807,4 +1810,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"このアプリはバックアップと復元に対応していないため、ショートカットを復元できませんでした"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"アプリの署名が一致しないため、ショートカットを復元できませんでした"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"ショートカットを復元できませんでした"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"ショートカットは無効になっています"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"アンインストール"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"このまま起動"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"有害なアプリをアンインストールしますか?"</string>
</resources>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 40e9414..45ed466 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"მალსახმობის აღდგენა ვერ მოხერხდა, რადგან ამ აპის მიერ მხარდაუჭერელია სარეზერვო ასლით აღდგენა"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"მალსახმობის აღდგენა ვერ მოხერხდა აპის ხელმოწერის შეუსაბამობის გამო"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"მალსახმობის აღდგენა ვერ მოხერხდა"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index d428ff0..51c5147 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1808,4 +1808,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Қолданба сақтық көшірме жасау мен қалпына келтіруді қолдамайтындықтан, таңбаша қалпына келтірілмеді"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Қолтаңба сәйкес келмейтіндіктен, таңбаша қалпына келтірілмеді"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Таңбаша қалпына келтірілмеді"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 889ea5e..c266416 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1809,4 +1809,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"មិនអាចស្តារផ្លូវកាត់បានទេ ដោយសារកម្មវិធីមិនស្គាល់ការបម្រុងទុក និងការស្តារ"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"មិនអាចស្តារផ្លូវកាត់បានទេ ដោយសារការស៊ីញ៉េកម្មវិធីមិនត្រូវគ្នា"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"មិនអាចស្តារផ្លូវកាត់បានទេ"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index d751c13..b9539ea 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -217,6 +217,8 @@
<string name="global_action_emergency" msgid="7112311161137421166">"ತುರ್ತು"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"ದೋಷದ ವರದಿ"</string>
<string name="global_action_logout" msgid="935179188218826050">"ಸೆಷನ್ ಅಂತ್ಯಗೊಳಿಸಿ"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="bugreport_title" msgid="2667494803742548533">"ದೋಷ ವರದಿ ರಚಿಸಿ"</string>
<string name="bugreport_message" msgid="398447048750350456">"ನಿಮ್ಮ ಸಾಧನದ ಪ್ರಸ್ತುತ ಸ್ಥಿತಿಯ ಕುರಿತು ಮಾಹಿತಿಯನ್ನು ಸಂಗ್ರಹಿಸಿಕೊಳ್ಳುವುದರ ಜೊತೆ ಇ-ಮೇಲ್ ರೂಪದಲ್ಲಿ ನಿಮಗೆ ರವಾನಿಸುತ್ತದೆ. ಇದು ದೋಷ ವರದಿಯನ್ನು ಪ್ರಾರಂಭಿಸಿದ ಸಮಯದಿಂದ ಅದನ್ನು ಕಳುಹಿಸುವವರೆಗೆ ಸ್ವಲ್ಪ ಸಮಯವನ್ನು ತೆಗೆದುಕೊಳ್ಳುತ್ತದೆ; ದಯವಿಟ್ಟು ತಾಳ್ಮೆಯಿಂದಿರಿ."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ಪರಸ್ಪರ ಸಂವಹನ ವರದಿ"</string>
@@ -1123,6 +1125,10 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"ವೈ‑ಫೈ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಆನ್ ಆಗುತ್ತದೆ"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"ನೀವು ಉಳಿಸಿದ ಅಧಿಕ ಗುಣಮಟ್ಟದ ನೆಟ್ವರ್ಕ್ ಸಮೀಪದಲ್ಲಿದ್ದಾಗ"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"ಮತ್ತೆ ಆನ್ ಮಾಡಲು ಹಿಂತಿರುಗಬೇಡಿ"</string>
+ <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) -->
+ <skip />
+ <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) -->
+ <skip />
<string name="wifi_available_sign_in" msgid="9157196203958866662">"ವೈ-ಫೈ ನೆಟ್ವರ್ಕ್ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"ನೆಟ್ವರ್ಕ್ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1808,4 +1814,11 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"ಅಪ್ಲಿಕೇಶನ್ ಬ್ಯಾಕಪ್ ಮತ್ತು ಪುನಃಸ್ಥಾಪನೆಯನ್ನು ಬೆಂಬಲಿಸದಿರುವುದರಿಂದ ಶಾರ್ಟ್ಕಟ್ ಅನ್ನು ಪುನಃಸ್ಥಾಪನೆ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"ಅಪ್ಲಿಕೇಶನ್ ಸಹಿ ಹೊಂದಿಕೆಯಾಗದ ಕಾರಣದಿಂದ ಶಾರ್ಟ್ಕಟ್ ಅನ್ನು ಪುನಃಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"ಶಾರ್ಟ್ಕಟ್ ಅನ್ನು ಪುನಃ ಸ್ಥಾಪನೆ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"ಶಾರ್ಟ್ಕಟ್ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_title (2229996292333310435) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 922f02f..221a233 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"앱이 백업 및 복원을 지원하지 않으므로 바로가기를 복원할 수 없습니다"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"앱 서명이 일치하지 않아 바로가기를 복원할 수 없습니다"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"바로가기를 복원할 수 없습니다"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index f3f7222..1bce491 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1809,4 +1809,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Колдонмо камдык көчүрмөнү сактоо жана калыбына келтирүү функцияларын колдобогондуктан кыска жол калыбына келтирилбей койду"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Колдонмонун колтамгасы дал келбегендиктен кыска жол калыбына келтирилбей койду"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Кыска жол калыбына келтирилбей койду"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index bc7e339..9f7a28f 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"ບໍ່ສາມາດກູ້ທາງລັດຂຶ້ນມາໄດ້ເນື່ອງຈາກແອັບບໍ່ຮອງຮັບການສຳຮອງ ແລະ ກູ້ຂໍ້ມູນ"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"ບໍ່ສາມາດກູ້ທາງລັດຄືນມາໄດ້ເນື່ອງຈາກລາຍເຊັນແອັບບໍ່ກົງກັນ"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"ບໍ່ສາມາດກູ້ທາງລັດຄືນມາໄດ້"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 2f5014e..e248f45 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -221,6 +221,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Skambutis pagalbos numeriu"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Pranešimas apie riktą"</string>
<string name="global_action_logout" msgid="935179188218826050">"Baigti seansą"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Ekrano kopija"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Pranešti apie riktą"</string>
<string name="bugreport_message" msgid="398447048750350456">"Bus surinkta informacija apie dabartinę įrenginio būseną ir išsiųsta el. pašto pranešimu. Šiek tiek užtruks, kol pranešimas apie riktą bus paruoštas siųsti; būkite kantrūs."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interakt. ataskaita"</string>
@@ -1167,6 +1168,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"„Wi‑Fi“ bus įjungtas automatiškai"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Kai būsite netoli išsaugoto aukštos kokybės tinklo"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Neįjunkite vėl"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"„Wi‑Fi“ įjungtas automatiškai"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Esate netoli išsaugoto tinklo: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Prisijungti prie „Wi-Fi“ tinklo"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Prisijungti prie tinklo"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1877,4 +1880,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Nepavyko atkurti sparčiojo klavišo, nes programa nepalaiko atsarginės kopijos kūrimo ir atkūrimo funkcijų"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Nepavyko atkurti sparčiojo klavišo, nes programos parašas neatitinka"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Nepavyko atkurti sparčiojo klavišo"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Spartusis klavišas išjungtas"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Pašalinti"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Vis tiek paleisti"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"Pašalinti žalingą programą?"</string>
</resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index c2199a8..2d98ade 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1842,4 +1842,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Nevarēja atjaunot saīsni, jo lietotnē netiek atbalstīta dublēšana un atjaunošana."</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Saīsni nevarēja atjaunot lietotnes paraksta neatbilstības dēļ."</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Nevarēja atjaunot saīsni."</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-mcc204-mnc04/config.xml b/core/res/res/values-mcc204-mnc04/config.xml
index c66ed12..4a3bf22 100755
--- a/core/res/res/values-mcc204-mnc04/config.xml
+++ b/core/res/res/values-mcc204-mnc04/config.xml
@@ -25,15 +25,5 @@
-->
<integer name="config_mobile_mtu">1358</integer>
- <!--Thresholds for LTE dbm in status bar-->
- <integer-array translatable="false" name="config_lteDbmThresholds">
- <item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
- <item>-115</item> <!-- SIGNAL_STRENGTH_POOR -->
- <item>-105</item> <!-- SIGNAL_STRENGTH_MODERATE -->
- <item>-95</item> <!-- SIGNAL_STRENGTH_GOOD -->
- <item>-85</item> <!-- SIGNAL_STRENGTH_GREAT -->
- <item>-44</item>
- </integer-array>
-
<string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true;BAE0000000000000</string>
</resources>
diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml
index 04f182e..cc7daa8 100755
--- a/core/res/res/values-mcc311-mnc480/config.xml
+++ b/core/res/res/values-mcc311-mnc480/config.xml
@@ -51,16 +51,6 @@
<bool name="config_auto_attach_data_on_creation">false</bool>
- <!--Thresholds for LTE dbm in status bar-->
- <integer-array translatable="false" name="config_lteDbmThresholds">
- <item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
- <item>-115</item> <!-- SIGNAL_STRENGTH_POOR -->
- <item>-105</item> <!-- SIGNAL_STRENGTH_MODERATE -->
- <item>-95</item> <!-- SIGNAL_STRENGTH_GOOD -->
- <item>-85</item> <!-- SIGNAL_STRENGTH_GREAT -->
- <item>-44</item>
- </integer-array>
-
<string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true</string>
<bool name="config_use_sim_language_file">true</bool>
diff --git a/core/res/res/values-mcc505-mnc01/config.xml b/core/res/res/values-mcc505-mnc01/config.xml
index 5a5b8f7..bc088d1 100644
--- a/core/res/res/values-mcc505-mnc01/config.xml
+++ b/core/res/res/values-mcc505-mnc01/config.xml
@@ -31,16 +31,6 @@
<item>9</item>
</integer-array>
- <!--Thresholds for LTE dbm in status bar-->
- <integer-array translatable="false" name="config_lteDbmThresholds">
- <item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
- <item>-120</item> <!-- SIGNAL_STRENGTH_POOR -->
- <item>-115</item> <!-- SIGNAL_STRENGTH_MODERATE -->
- <item>-100</item> <!-- SIGNAL_STRENGTH_GOOD -->
- <item>-90</item> <!-- SIGNAL_STRENGTH_GREAT -->
- <item>-44</item>
- </integer-array>
-
<!-- Configure mobile network MTU. Carrier specific value is set here.
-->
<integer name="config_mobile_mtu">1400</integer>
diff --git a/core/res/res/values-mcc505-mnc11/config.xml b/core/res/res/values-mcc505-mnc11/config.xml
deleted file mode 100644
index 6d085c1..0000000
--- a/core/res/res/values-mcc505-mnc11/config.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2017, 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 my 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!--Thresholds for LTE dbm in status bar-->
- <integer-array translatable="false" name="config_lteDbmThresholds">
- <item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
- <item>-120</item> <!-- SIGNAL_STRENGTH_POOR -->
- <item>-115</item> <!-- SIGNAL_STRENGTH_MODERATE -->
- <item>-100</item> <!-- SIGNAL_STRENGTH_GOOD -->
- <item>-90</item> <!-- SIGNAL_STRENGTH_GREAT -->
- <item>-44</item>
- </integer-array>
-</resources>
diff --git a/core/res/res/values-mcc505-mnc71/config.xml b/core/res/res/values-mcc505-mnc71/config.xml
deleted file mode 100644
index 6d085c1..0000000
--- a/core/res/res/values-mcc505-mnc71/config.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2017, 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 my 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!--Thresholds for LTE dbm in status bar-->
- <integer-array translatable="false" name="config_lteDbmThresholds">
- <item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
- <item>-120</item> <!-- SIGNAL_STRENGTH_POOR -->
- <item>-115</item> <!-- SIGNAL_STRENGTH_MODERATE -->
- <item>-100</item> <!-- SIGNAL_STRENGTH_GOOD -->
- <item>-90</item> <!-- SIGNAL_STRENGTH_GREAT -->
- <item>-44</item>
- </integer-array>
-</resources>
diff --git a/core/res/res/values-mcc505-mnc72/config.xml b/core/res/res/values-mcc505-mnc72/config.xml
deleted file mode 100644
index 6d085c1..0000000
--- a/core/res/res/values-mcc505-mnc72/config.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2017, 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 my 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!--Thresholds for LTE dbm in status bar-->
- <integer-array translatable="false" name="config_lteDbmThresholds">
- <item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
- <item>-120</item> <!-- SIGNAL_STRENGTH_POOR -->
- <item>-115</item> <!-- SIGNAL_STRENGTH_MODERATE -->
- <item>-100</item> <!-- SIGNAL_STRENGTH_GOOD -->
- <item>-90</item> <!-- SIGNAL_STRENGTH_GREAT -->
- <item>-44</item>
- </integer-array>
-</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index e1e2b43..67aabea 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1810,4 +1810,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Не можеше да се врати кратенката бидејќи апликацијата не поддржува бекап и враќање"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Не можеше да се врати кратенката бидејќи потписот на апликацијата не се совпаѓа"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Не можеше да се врати кратенката"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 6a8cea1..ed2dc02 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -217,6 +217,8 @@
<string name="global_action_emergency" msgid="7112311161137421166">"അടിയന്തിരാവശ്യം"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"ബഗ് റിപ്പോർട്ട്"</string>
<string name="global_action_logout" msgid="935179188218826050">"സെഷൻ അവസാനിപ്പിക്കുക"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="bugreport_title" msgid="2667494803742548533">"ബഗ് റിപ്പോർട്ട് എടുക്കുക"</string>
<string name="bugreport_message" msgid="398447048750350456">"ഒരു ഇമെയിൽ സന്ദേശമായി അയയ്ക്കുന്നതിന്, ഇത് നിങ്ങളുടെ നിലവിലെ ഉപകരണ നിലയെക്കുറിച്ചുള്ള വിവരങ്ങൾ ശേഖരിക്കും. ബഗ് റിപ്പോർട്ട് ആരംഭിക്കുന്നതിൽ നിന്ന് ഇത് അയയ്ക്കാനായി തയ്യാറാകുന്നതുവരെ അൽപ്പസമയമെടുക്കും; ക്ഷമയോടെ കാത്തിരിക്കുക."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ഇന്റരാക്റ്റീവ് റിപ്പോർട്ട്"</string>
@@ -1123,6 +1125,10 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"വൈഫൈ സ്വമേധയാ ഓണാകും"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"നിങ്ങൾ ഉയർന്ന നിലവാരമുള്ള സംരക്ഷിക്കപ്പെട്ട നെറ്റ്വർക്കിനരികിലെത്തുമ്പോൾ"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"തിരികെ ഓണാക്കരുത്"</string>
+ <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) -->
+ <skip />
+ <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) -->
+ <skip />
<string name="wifi_available_sign_in" msgid="9157196203958866662">"വൈഫൈ നെറ്റ്വർക്കിലേക്ക് സൈൻ ഇൻ ചെയ്യുക"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"നെറ്റ്വർക്കിലേക്ക് സൈൻ ഇൻ ചെയ്യുക"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1808,4 +1814,11 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"ആപ്പ് \'ബാക്കപ്പും പുനഃസ്ഥാപിക്കലും\' പിന്തുണയ്ക്കാത്തതിനാൽ കുറുക്കുവഴി പുനഃസ്ഥാപിക്കാനായില്ല"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"ആപ്പ് സിഗ്നേച്ചർ പൊരുത്തപ്പെടാത്തതിനാൽ കുറുക്കുവഴി പുനഃസ്ഥാപിക്കാനായില്ല"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"കുറുക്കുവഴി പുനഃസ്ഥാപിക്കാനായില്ല"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"കുറുക്കുവഴി പ്രവർത്തനരഹിതമാണ്"</string>
+ <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_title (2229996292333310435) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 53740f94..dbbb29e 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1805,4 +1805,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Апп нөөцлөлт, сэргээлтийг дэмждэггүй тул товчлолыг сэргээж чадсангүй"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Аппын гарын үсэг таарахгүй байгаа тул товчлолыг сэргээж чадсангүй"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Товчлолыг сэргээж чадсангүй"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 398ad0d..9e36c1d 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -217,6 +217,8 @@
<string name="global_action_emergency" msgid="7112311161137421166">"आणीबाणी"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"बग रीपोर्ट"</string>
<string name="global_action_logout" msgid="935179188218826050">"सेशन समाप्त करा"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="bugreport_title" msgid="2667494803742548533">"बग रीपोर्ट घ्या"</string>
<string name="bugreport_message" msgid="398447048750350456">"ई-मेल संदेश म्हणून पाठविण्यासाठी, हे तुमच्या सद्य डिव्हाइस स्थितीविषयी माहिती संकलित करेल. बग रीपोर्ट सुरू करण्यापासून तो पाठविण्यापर्यंत थोडा वेळ लागेल; कृपया धीर धरा."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"परस्परसंवादी अहवाल"</string>
@@ -1123,6 +1125,10 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"वाय-फाय आपोआप चालू होईल"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"तुम्ही जेव्हा सेव्ह केलेल्या उच्च दर्जाच्या नेटवर्कजवळ असाल तेव्हा"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"पुन्हा चालू करू नका"</string>
+ <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) -->
+ <skip />
+ <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) -->
+ <skip />
<string name="wifi_available_sign_in" msgid="9157196203958866662">"वाय-फाय नेटवर्कमध्ये साइन इन करा"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"नेटवर्कवर साइन इन करा"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1808,4 +1814,11 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"अॅप बॅकअप आणि रिस्टोअर करण्यास सपोर्ट देत नसल्यामुळे शॉर्टकट रिस्टोअर करू शकलो नाही"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"अॅप स्वाक्षरी न जुळल्यामुळे शॉर्टकट रिस्टोअर करू शकलो नाही"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"शॉर्टकट रिस्टोअर करू शकलो नाही"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"शॉर्टकट बंद केलेला आहे"</string>
+ <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_title (2229996292333310435) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 574c775..f7e18ff 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Tidak dapat memulihkan pintasan kerana apl tidak menyokong sandaran dan segerakan"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Tidak dapat memulihkan pintasan kerana ketakpadanan tandatangan apl"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Tidak dapat memulihkan pintasan"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 9dc49cf..5f67641 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -217,6 +217,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"အရေးပေါ်"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"အမှားရှာဖွေပြင်ဆင်မှုမှတ်တမ်း"</string>
<string name="global_action_logout" msgid="935179188218826050">"သတ်မှတ်ပေးထားသည့်အချိန် ပြီးဆုံးပြီ"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"ဖန်သားပြင်ဓာတ်ပုံ"</string>
<string name="bugreport_title" msgid="2667494803742548533">"အမှားရှာဖွေပြင်ဆင်မှုမှတ်တမ်းအား ယူရန်"</string>
<string name="bugreport_message" msgid="398447048750350456">"သင့်ရဲ့ လက်ရှိ စက်အခြေအနေ အချက်အလက်များကို အီးမေးလ် အနေဖြင့် ပေးပို့ရန် စုဆောင်းပါမည်။ အမှားရှာဖွေပြင်ဆင်မှုမှတ်တမ်းမှ ပေးပို့ရန် အသင့်ဖြစ်သည်အထိ အချိန် အနည်းငယ်ကြာမြင့်မှာ ဖြစ်သဖြင့် သည်းခံပြီး စောင့်ပါရန်"</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"လက်ငင်းတုံ့ပြန်နိုင်သည့် အစီရင်ခံချက်"</string>
@@ -1123,6 +1124,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi ကို အလိုအလျောက် ပြန်ဖွင့်ပေးလိမ့်ပါမည်"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"သိမ်းဆည်းထားသည့် အရည်အသွေးမြင့်ကွန်ရက်များအနီးသို့ ရောက်ရှိသည့်အခါ"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"ပြန်မဖွင့်ပါနှင့်"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi ကို အလိုအလျောက် ဖွင့်ထားသည်"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"သင်သည် သိမ်းထားသည့် ကွန်ရက်အနီးတွင် ရှိသည်− <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"ဝိုင်ဖိုင်ကွန်ရက်သို့ လက်မှတ်ထိုးဝင်ပါ"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"ကွန်ယက်သို့ လက်မှတ်ထိုးဝင်ရန်"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1808,4 +1811,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"အက်ပ်သည် မိတ္တူကူးခြင်းနှင့် ပြန်ယူခြင်းကို ပံ့ပိုးခြင်းမရှိသည့်အတွက် ဖြတ်လမ်းလင့်ခ်ကို ပြန်ယူ၍မရပါ"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"အက်ပ်လက်မှတ် မတူညီသည့်အတွက် ဖြတ်လမ်းလင့်ခ်များကို ပြန်ယူ၍မရပါ"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"ဖြတ်လမ်းလင့်ခ်ကို ပြန်ယူ၍မရပါ"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"ဖြတ်လမ်းလင့်ခ် ပိတ်ထားသည်"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"ဖြုတ်ရန်"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"မည်သို့ပင်ဖြစ်စေ ဖွင့်ရန်"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"အန္တရာယ်ရှိသည့် အက်ပ်ကို ဖြုတ်လိုပါသလား။"</string>
</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index d360fa4..c3deb3e 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -217,6 +217,8 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Nødsituasjon"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Feilrapport"</string>
<string name="global_action_logout" msgid="935179188218826050">"Avslutt økten"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="bugreport_title" msgid="2667494803742548533">"Utfør feilrapport"</string>
<string name="bugreport_message" msgid="398447048750350456">"Informasjon om tilstanden til enheten din samles inn og sendes som en e-post. Det tar litt tid fra du starter feilrapporten til e-posten er klar, så vær tålmodig."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktiv rapport"</string>
@@ -1123,6 +1125,10 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi slås på automatisk"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Når du er i nærheten av et lagret nettverk av høy kvalitet"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ikke slå på igjen"</string>
+ <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) -->
+ <skip />
+ <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) -->
+ <skip />
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Logg på Wi-Fi-nettverket"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Logg på nettverk"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1807,4 +1813,11 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Kunne ikke gjenopprette snarveien fordi appen ikke støtter sikkerhetskopiering og gjenoppretting"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Kunne ikke gjenopprette snarveien på grunn av manglende samsvar for appsignaturen"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Kunne ikke gjenopprette snarveien"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Snarveien er slått av"</string>
+ <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_title (2229996292333310435) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 3d05239..71e1258 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -217,6 +217,8 @@
<string name="global_action_emergency" msgid="7112311161137421166">"आपतकालीन"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"बग रिपोर्ट"</string>
<string name="global_action_logout" msgid="935179188218826050">"सत्रको अन्त्य गर्नुहोस्"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="bugreport_title" msgid="2667494803742548533">"बग रिपोर्ट लिनुहोस्"</string>
<string name="bugreport_message" msgid="398447048750350456">"एउटा इमेल सन्देशको रूपमा पठाउनलाई यसले तपाईँको हालैको उपकरणको अवस्थाको बारेमा सूचना जम्मा गर्ने छ। बग रिपोर्ट सुरु गरेदेखि पठाउन तयार नभएसम्म यसले केही समय लिन्छ; कृपया धैर्य गर्नुहोस्।"</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"अन्तरक्रियामूलक रिपोर्ट"</string>
@@ -1129,6 +1131,10 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi स्वतः सक्रिय हुनेछ"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"तपाईं कुनै सुरक्षित गरिएको उच्च गुणस्तरीय नेटवर्कको नजिक हुनुभएको अवस्थामा"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"फेरि सक्रिय नगर्नुहोला"</string>
+ <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) -->
+ <skip />
+ <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) -->
+ <skip />
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Wi-Fi नेटवर्कमा साइन इन गर्नुहोस्"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"सञ्जालमा साइन इन गर्नुहोस्"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1813,4 +1819,11 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"अनुप्रयोगले ब्याकअप तथा पुनर्स्थापनालाई समर्थन नगर्ने हुँदा सर्टकट पुनर्स्थापित गर्न सकिएन"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"अनुप्रयोगमा प्रयोग गरिने हस्ताक्षर नमिल्ने हुँदा सर्टकट पुनर्स्थापित गर्न सकिएन"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"सर्टकट पुनर्स्थापित गर्न सकिएन"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"सर्टकट असक्षम पारिएको छ"</string>
+ <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_title (2229996292333310435) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 725fe8e..1d8619c 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Kan snelkoppeling niet herstellen omdat de app geen ondersteuning biedt voor \'Back-up maken en terugzetten\'"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Kan snelkoppeling niet herstellen vanwege een niet-overeenkomende app-ondertekening"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Kan snelkoppeling niet herstellen"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index cf90550..8a11b9f 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -217,6 +217,8 @@
<string name="global_action_emergency" msgid="7112311161137421166">"ਸੰਕਟਕਾਲ"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"ਬਗ ਰਿਪੋਰਟ"</string>
<string name="global_action_logout" msgid="935179188218826050">"ਸੈਸ਼ਨ ਸਮਾਪਤ ਕਰੋ"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="bugreport_title" msgid="2667494803742548533">"ਬਗ ਰਿਪੋਰਟ ਲਓ"</string>
<string name="bugreport_message" msgid="398447048750350456">"ਇਹ ਇੱਕ ਈਮੇਲ ਸੁਨੇਹਾ ਭੇਜਣ ਲਈ, ਤੁਹਾਡੇ ਵਰਤਮਾਨ ਡੀਵਾਈਸ ਬਾਰੇ ਜਾਣਕਾਰੀ ਇਕੱਠੀ ਕਰੇਗਾ। ਬੱਗ ਰਿਪੋਰਟ ਸ਼ੁਰੂ ਕਰਨ ਵਿੱਚ ਥੋੜ੍ਹਾ ਸਮਾਂ ਲੱਗੇਗਾ ਜਦੋਂ ਤੱਕ ਇਹ ਭੇਜੇ ਜਾਣ ਲਈ ਤਿਆਰ ਨਾ ਹੋਵੇ, ਕਿਰਪਾ ਕਰਕੇ ਧੀਰਜ ਰੱਖੋ।"</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ਅੰਤਰਕਿਰਿਆਤਮਕ ਰਿਪੋਰਟ"</string>
@@ -1123,6 +1125,10 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"ਵਾਈ‑ਫਾਈ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਚੱਲ ਪਵੇਗਾ"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"ਜਦੋਂ ਤੁਸੀਂ ਕਿਸੇ ਰੱਖਿਅਤ ਕੀਤੇ ਉੱਚ-ਗੁਣਵੱਤਾ ਵਾਲੇ ਨੈੱਟਵਰਕ ਦੇ ਨੇੜੇ ਹੋਵੋ"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"ਵਾਪਸ ਚਾਲੂ ਨਾ ਕਰੋ"</string>
+ <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) -->
+ <skip />
+ <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) -->
+ <skip />
<string name="wifi_available_sign_in" msgid="9157196203958866662">"ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"ਨੈੱਟਵਰਕ \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1808,4 +1814,11 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"ਸ਼ਾਰਟਕੱਟ ਮੁੜ-ਬਹਾਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ ਕਿਉਂਕਿ ਐਪ \'ਬੈਕਅੱਪ ਅਤੇ ਮੁੜ-ਬਹਾਲੀ\' ਨਾਲ ਕੰਮ ਨਹੀਂ ਕਰਦੀ"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"ਐਪ ਹਸਤਾਖਰ ਦਾ ਮੇਲ ਨਾ ਹੋਣ ਕਾਰਨ ਸ਼ਾਰਟਕੱਟ ਮੁੜ-ਬਹਾਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"ਸ਼ਾਰਟਕੱਟ ਮੁੜ-ਬਹਾਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"ਸ਼ਾਰਟਕੱਟ ਬੰਦ ਕੀਤਾ ਹੋਇਆ ਹੈ"</string>
+ <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_title (2229996292333310435) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 412d5a7..108b87b 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1877,4 +1877,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Nie można przywrócić skrótu, bo aplikacja nie obsługuje tworzenia i przywracania kopii zapasowej"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Nie można przywrócić skrótu z powodu niezgodności podpisu aplikacji"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Nie można przywrócić skrótu"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index bf83d77..5201f14 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -217,6 +217,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Emergência"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Relatório de bugs"</string>
<string name="global_action_logout" msgid="935179188218826050">"Finalizar sessão"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Captura de tela"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Obter relatório de bugs"</string>
<string name="bugreport_message" msgid="398447048750350456">"Isto coletará informações sobre o estado atual do dispositivo para enviá-las em uma mensagem de e-mail. Após iniciar o relatório de bugs, será necessário aguardar algum tempo até que esteja pronto para ser enviado."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Relatório interativo"</string>
@@ -1123,6 +1124,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"O Wi‑Fi será ativado automaticamente"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Quando você estiver perto de uma rede salva de alta qualidade"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Não ativar novamente"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi ativado automaticamente"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Você está perto de uma rede salva: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Fazer login na rede Wi-Fi"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Fazer login na rede"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1807,4 +1810,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Não foi possível restaurar o atalho porque o app não é compatível com backup e restauração"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Não foi possível restaurar o atalho devido à incompatibilidade de assinatura de apps"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Não foi possível restaurar o atalho"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"O atalho está desativado"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Desinstalar"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Iniciar mesmo assim"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"Desinstalar app nocivo?"</string>
</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 31b67a0..d6b888a 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -217,6 +217,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Emergência"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Relatório de erros"</string>
<string name="global_action_logout" msgid="935179188218826050">"Terminar sessão"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Captura de ecrã"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Criar relatório de erros"</string>
<string name="bugreport_message" msgid="398447048750350456">"Será recolhida informação sobre o estado atual do seu dispositivo a enviar através de uma mensagem de email. Demorará algum tempo até que o relatório de erro esteja pronto para ser enviado. Aguarde um pouco."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Relatório interativo"</string>
@@ -804,8 +805,8 @@
<string name="granularity_label_link" msgid="5815508880782488267">"link"</string>
<string name="granularity_label_line" msgid="5764267235026120888">"linha"</string>
<string name="factorytest_failed" msgid="5410270329114212041">"O teste de fábrica falhou"</string>
- <string name="factorytest_not_system" msgid="4435201656767276723">"A acção FACTORY_TEST apenas é suportada para pacotes instalados em /system/app."</string>
- <string name="factorytest_no_action" msgid="872991874799998561">"Não foi localizado qualquer pacote que forneça a acção FACTORY_TEST."</string>
+ <string name="factorytest_not_system" msgid="4435201656767276723">"A ação FACTORY_TEST apenas é suportada para pacotes instalados em /system/app."</string>
+ <string name="factorytest_no_action" msgid="872991874799998561">"Não foi localizado qualquer pacote que forneça a ação FACTORY_TEST."</string>
<string name="factorytest_reboot" msgid="6320168203050791643">"Reiniciar"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"A página em \"<xliff:g id="TITLE">%s</xliff:g>\" indica:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
@@ -1023,7 +1024,7 @@
<string name="whichImageCaptureApplication" msgid="3680261417470652882">"Capturar imagem com"</string>
<string name="whichImageCaptureApplicationNamed" msgid="8619384150737825003">"Capturar imagem com o %1$s"</string>
<string name="whichImageCaptureApplicationLabel" msgid="6390303445371527066">"Capturar imagem"</string>
- <string name="alwaysUse" msgid="4583018368000610438">"Utilizar por predefinição para esta acção."</string>
+ <string name="alwaysUse" msgid="4583018368000610438">"Utilizar por predefinição para esta ação."</string>
<string name="use_a_different_app" msgid="8134926230585710243">"Utilizar outra aplicação"</string>
<string name="clearDefaultHintMsg" msgid="3252584689512077257">"Limpar a predefinição nas Definições do Sistema > Aplicações > Transferidas."</string>
<string name="chooseActivity" msgid="7486876147751803333">"Escolha uma ação"</string>
@@ -1123,6 +1124,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"O Wi‑Fi será ativado automaticamente"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Quando estiver próximo de uma rede de alta qualidade guardada."</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Não reativar"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi ativado automaticamente"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Está perto de uma rede guardada: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>."</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Iniciar sessão na rede Wi-Fi"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Início de sessão na rede"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1807,4 +1810,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Não foi possível restaurar o atalho porque a aplicação não é compatível com a funcionalidade de cópia de segurança e restauro."</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Não foi possível restaurar o atalho devido a uma falha de correspondência entre as assinaturas das aplicações."</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Não foi possível restaurar o atalho."</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"O atalho está desativado."</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Desinstalar"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Iniciar mesmo assim"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"Pretende desinstalar a aplicação prejudicial?"</string>
</resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index bf83d77..5201f14 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -217,6 +217,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Emergência"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Relatório de bugs"</string>
<string name="global_action_logout" msgid="935179188218826050">"Finalizar sessão"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Captura de tela"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Obter relatório de bugs"</string>
<string name="bugreport_message" msgid="398447048750350456">"Isto coletará informações sobre o estado atual do dispositivo para enviá-las em uma mensagem de e-mail. Após iniciar o relatório de bugs, será necessário aguardar algum tempo até que esteja pronto para ser enviado."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Relatório interativo"</string>
@@ -1123,6 +1124,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"O Wi‑Fi será ativado automaticamente"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Quando você estiver perto de uma rede salva de alta qualidade"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Não ativar novamente"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi ativado automaticamente"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Você está perto de uma rede salva: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Fazer login na rede Wi-Fi"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Fazer login na rede"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1807,4 +1810,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Não foi possível restaurar o atalho porque o app não é compatível com backup e restauração"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Não foi possível restaurar o atalho devido à incompatibilidade de assinatura de apps"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Não foi possível restaurar o atalho"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"O atalho está desativado"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Desinstalar"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Iniciar mesmo assim"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"Desinstalar app nocivo?"</string>
</resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 4f15c33..22b1bde 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -219,6 +219,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Urgență"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Raport despre erori"</string>
<string name="global_action_logout" msgid="935179188218826050">"Încheiați sesiunea"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Captură de ecran"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Executați un raport despre erori"</string>
<string name="bugreport_message" msgid="398447048750350456">"Acest raport va colecta informații despre starea actuală a dispozitivului, pentru a le trimite într-un e-mail. Aveți răbdare după pornirea raportului despre erori până când va fi gata de trimis."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Raport interactiv"</string>
@@ -1145,6 +1146,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi se va activa automat"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Când vă aflați lângă o rețea salvată, de înaltă calitate"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Nu reactivați"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi-Fi s-a activat automat"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Vă aflați lângă o rețea salvată: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Conectați-vă la rețeaua Wi-Fi"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Conectați-vă la rețea"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1842,4 +1845,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Nu s-a putut restabili comanda rapidă deoarece aplicația nu acceptă backupul și restabilirea"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Nu s-a putut restabili comanda rapidă din cauza nepotrivirii semnăturii aplicației"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Nu s-a putut restabili comanda rapidă"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Comanda rapidă este dezactivată"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Dezinstalați"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Lansați oricum"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"Dezinstalați aplicația dăunătoare?"</string>
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 60ad441..70dc651 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -221,6 +221,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Экстренный вызов"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Отчет об ошибке"</string>
<string name="global_action_logout" msgid="935179188218826050">"Закончить сеанс"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Скриншот"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Отчет об ошибке"</string>
<string name="bugreport_message" msgid="398447048750350456">"Информация о текущем состоянии вашего устройства будет собрана и отправлена по электронной почте. Подготовка отчета займет некоторое время."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Интерактивный отчет"</string>
@@ -1167,6 +1168,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi включится автоматически"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Когда вы будете в зоне действия сохраненной сети с хорошим сигналом."</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Не включать снова"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi включен автоматически"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Вы находитесь в зоне действия сохраненной сети <xliff:g id="NETWORK_SSID">%1$s</xliff:g>."</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Подключение к Wi-Fi"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Регистрация в сети"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1877,4 +1880,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Не удалось восстановить ярлык: приложение не поддерживает резервное копирование"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Не удалось восстановить ярлык: некорректная подпись приложения"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Не удалось восстановить ярлык"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Ярлык отключен"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Удалить"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Запустить все равно"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"Удалите опасное приложение"</string>
</resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 5b0eda3..609f99f 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -217,6 +217,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"හදිසි"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"දෝෂ වර්තාව"</string>
<string name="global_action_logout" msgid="935179188218826050">"සැසිය අවසන් කරන්න"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"තිර රුව"</string>
<string name="bugreport_title" msgid="2667494803742548533">"දෝෂ වාර්තාවක් ගන්න"</string>
<string name="bugreport_message" msgid="398447048750350456">"ඊ-තැපැල් පණිවිඩයක් ලෙස යැවීමට මෙය ඔබගේ වත්මන් උපාංග තත්වය ගැන තොරතුරු එකතු කරනු ඇත. දෝෂ වාර්තාව ආරම්භ කර එය යැවීමට සූදානම් කරන තෙක් එයට කිසියම් කාලයක් ගතවනු ඇත; කරුණාකර ඉවසන්න."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"අන්තර්ක්රියා වාර්."</string>
@@ -1125,6 +1126,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi ස්වයංක්රියව ක්රියාත්මක වනු ඇත"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"ඔබ උසස් තත්ත්වයේ සුරැකි ජාලයක් අවට සිටින විට"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"නැවත ක්රියාත්මක නොකරන්න"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi ස්වයංක්රියව ක්රියාත්මක කරන ලදි"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"ඔබ සුරැකි ජාලයක් අවට සිටී: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Wi-Fi ජාලයට පුරනය වන්න"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"ජාලයට පුරනය වන්න"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1809,4 +1812,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"යෙදුම උපස්ථ සහ ප්රතිසාධනය සඳහා සහාය නොදක්වන බැවින් කෙටි මග ප්රතිසාධනය කළ නොහැකි විය"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"යෙදුම් අත්සන නොගැළපෙන බැවින් කෙටි මග ප්රතිසාධනය කළ නොහැකි විය"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"කෙටි මග ප්රතිසාධනය කළ නොහැකි විය"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"කෙටි මග අබල කර ඇත"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"අස්ථාපනය කරන්න"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"කෙසේ වුවත් ආරම්භ කරන්න"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"හානිකර යෙදුම අස්ථාපනය කරන්නද?"</string>
</resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 1f11fb4..1fb90ff 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1877,4 +1877,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Odkaz sa nepodarilo obnoviť, pretože aplikácia nepodporuje zálohovanie a obnovu"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Odkaz sa nepodarilo obnoviť pre nesúlad podpisov aplikácie"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Odkaz sa nepodarilo obnoviť"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index e8f89a5..3c89e75 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -221,6 +221,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Klic v sili"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Poročilo o napakah"</string>
<string name="global_action_logout" msgid="935179188218826050">"Končaj sejo"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Posnetek zaslona"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Ustvari poročilo o napakah"</string>
<string name="bugreport_message" msgid="398447048750350456">"S tem bodo zbrani podatki o trenutnem stanju naprave, ki bodo poslani v e-poštnem sporočilu. Izvedba poročila o napakah in priprava trajata nekaj časa, zato bodite potrpežljivi."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktivno poročilo"</string>
@@ -1167,6 +1168,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Povezava Wi‑Fi se bo samodejno vklopila"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Ko ste v bližini zanesljivega shranjenega omrežja"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ne vklopi znova"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi je bil vklopljen samodejno"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Ste v bližini shranjenega omrežja: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Prijavite se v omrežje Wi-Fi"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Prijava v omrežje"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1877,4 +1880,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Bližnjice ni bilo mogoče obnoviti, ker aplikacija ne podpira varnostnega kopiranja in obnavljanja"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Bližnjice ni bilo mogoče obnoviti zaradi neujemanja podpisa aplikacije"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Bližnjice ni bilo mogoče obnoviti"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Bližnjica je onemogočena"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Odstrani"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Vseeno zaženi"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"Želite odstraniti škodljivo aplikacijo?"</string>
</resources>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index a03ac24..e6d60d4 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1808,4 +1808,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Nuk mund të restaurohej shkurtorja sepse aplikacioni nuk mbështet rezervimin dhe restaurimin"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Nuk mund të restaurohej shkurtorja për shkak të mospërputhjes së nënshkrimit të aplikacionit"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Nuk mund të restaurohej shkurtorja"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 8bec119..275db37 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1842,4 +1842,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Враћање пречице није успело јер апликација не подржава прављење резервне копије и враћање"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Враћање пречице није успело јер се потписи апликација не подударају"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Враћање пречице није успело"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 1f590d3..1633e43 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1205,7 +1205,7 @@
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Ett tillbehör med analog ljudutgång hittades"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Den anslutna enheten är inte kompatibel med mobilen. Tryck här om du vill veta mer."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-felsökning ansluten"</string>
- <string name="adb_active_notification_message" msgid="4948470599328424059">"Tryck om du vill inaktivera USB-felsökning."</string>
+ <string name="adb_active_notification_message" msgid="4948470599328424059">"Tryck för att inaktivera USB-felsökning."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Välj för att inaktivera USB-felsökning."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Felrapporten överförs …"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Vill du dela felrapporten?"</string>
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Det gick inte att återställa genvägen eftersom appen inte har stöd för säkerhetskopiering och återställning"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Det gick inte att återställa genvägen eftersom appens signatur inte stämmer"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Det gick inte att återställa genvägen"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index bd8b029..748f812 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -215,6 +215,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Dharura"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Ripoti ya hitilafu"</string>
<string name="global_action_logout" msgid="935179188218826050">"Maliza kipindi"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Picha ya skrini"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Chukua ripoti ya hitilafu"</string>
<string name="bugreport_message" msgid="398447048750350456">"Hii itakusanya maelezo kuhusu hali ya kifaa chako kwa sasa, na itume kama barua pepe. Itachukua muda mfupi tangu ripoti ya hitilafu ianze kuzalishwa hadi iwe tayari kutumwa; vumilia."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Ripoti ya kushirikiana"</string>
@@ -1121,6 +1122,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi itawashwa kiotomatiki"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Unapokuwa karibu na mtandao uliohifadhiwa wenye ubora wa juu"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Usiwashe tena"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi imewashwa kiotomatiki"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Uko karibu na mtandao uliohifadhiwa: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Ingia kwa mtandao wa Wi-Fi"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Ingia katika mtandao"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1805,4 +1808,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Imeshindwa kurejesha njia ya mkato kwa sababu programu haitumii kipengele cha hifadhi rudufu na kurejesha upya"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Imeshindwa kurejesha njia ya mkato kwa sababu ufunguo wako wa kuambatisha cheti kwenye programu haulingani"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Imeshindwa kurejesha njia ya mkato"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Njia ya mkato imezimwa"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Ondoa"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Fungua tu"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"Ungependa kuondoa programu hatari?"</string>
</resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 6d247f4..6014de4 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1808,4 +1808,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"காப்புப் பிரதி மற்றும் மீட்டமைவைப் பயன்பாடு ஆதரிக்காத காரணத்தால், ஷார்ட்கட்டை மீட்டமைக்க முடியவில்லை"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"பயன்பாட்டுச் சான்றுகள் பொருந்தாத காரணத்தினால், ஷார்ட்கட்டை மீட்டமைக்க முடியவில்லை"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"ஷார்ட்கட்டை மீட்டமைக்க முடியவில்லை"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index d39467b..589ac91 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -217,6 +217,8 @@
<string name="global_action_emergency" msgid="7112311161137421166">"అత్యవసరం"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"బగ్ నివేదిక"</string>
<string name="global_action_logout" msgid="935179188218826050">"సెషన్ను ముగించు"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="bugreport_title" msgid="2667494803742548533">"బగ్ నివేదికను సిద్ధం చేయి"</string>
<string name="bugreport_message" msgid="398447048750350456">"ఇది ఇ-మెయిల్ సందేశం రూపంలో పంపడానికి మీ ప్రస్తుత పరికర స్థితి గురించి సమాచారాన్ని సేకరిస్తుంది. బగ్ నివేదికను ప్రారంభించడం మొదలుకొని పంపడానికి సిద్ధం చేసే వరకు ఇందుకు కొంత సమయం పడుతుంది; దయచేసి ఓపిక పట్టండి."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ప్రభావశీల నివేదిక"</string>
@@ -1123,6 +1125,10 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi స్వయంచాలకంగా ఆన్ అవుతుంది"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"మీరు అధిక నాణ్యత గల సేవ్ చేసిన నెట్వర్క్కు సమీపంగా ఉన్నప్పుడు"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"తిరిగి ఆన్ చేయవద్దు"</string>
+ <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) -->
+ <skip />
+ <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) -->
+ <skip />
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Wi-Fi నెట్వర్క్కి సైన్ ఇన్ చేయండి"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"నెట్వర్క్కి సైన్ ఇన్ చేయండి"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1808,4 +1814,11 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"బ్యాకప్ మరియు పునరుద్ధరణకు యాప్ మద్దతు ఇవ్వని కారణంగా సత్వరమార్గాన్ని పునరుద్ధరించడం సాధ్యపడలేదు"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"యాప్ సంతకం సరిపోలని కారణంగా సత్వరమార్గాన్ని పునరుద్ధరించడం సాధ్యపడలేదు"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"సత్వరమార్గాన్ని పునరుద్ధరించడం సాధ్యపడలేదు"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"షార్ట్కట్ నిలిపివేయబడింది"</string>
+ <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_title (2229996292333310435) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 1bea4e4..d9a504b 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"คืนค่าทางลัดไม่ได้เนื่องจากแอปไม่รองรับการสำรองข้อมูลและคืนค่า"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"คืนค่าทางลัดไม่ได้เนื่องจากการลงนามแอปไม่ตรงกัน"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"คืนค่าทางลัดไม่ได้"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 2bc5780..98e169e 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Hindi ma-restore ang shortcut dahil hindi sinusuportahan ng app ang pag-back up at pag-restore"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Hindi ma-restore ang shortcut dahil hindi magkatugma ang signature ng app"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Hindi ma-restore ang shortcut"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 337d458..e2b1cb3 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Uygulama, yedekleme ve geri yüklemeyi desteklemediğinden kısayol geri yüklenemedi"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Uygulama imzası eşleşmediğinden kısayol geri yüklenemedi"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Kısayol geri yüklenemedi"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 5344dcf..0d70955 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -221,6 +221,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Екстрений виклик"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Звіт про помилки"</string>
<string name="global_action_logout" msgid="935179188218826050">"Завершити сеанс"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Знімок екрана"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Звіт про помилку"</string>
<string name="bugreport_message" msgid="398447048750350456">"Інформація про поточний стан вашого пристрою буде зібрана й надіслана електронною поштою. Підготовка звіту триватиме певний час."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Інтерактивний звіт"</string>
@@ -1167,6 +1168,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi вмикатиметься автоматично"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Коли ви поблизу збереженої мережі високої якості"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Не вмикати знову"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"З’єднання Wi-Fi увімкнулось автоматично"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Ви поблизу збереженої мережі: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Вхід у мережу Wi-Fi"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Вхід у мережу"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1877,4 +1880,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Не вдалося відновити ярлик, оскільки додаток не підтримує резервне копіювання та відновлення"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Не вдалося відновити ярлик, оскільки підписи додатків не збігаються"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Не вдалося відновити ярлик"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Ярлик вимкнено"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Видалити"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Усе одно запустити"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"Видалити шкідливий додаток?"</string>
</resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 4ced9cf..54b25b7 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -217,6 +217,8 @@
<string name="global_action_emergency" msgid="7112311161137421166">"ایمرجنسی"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"بگ کی اطلاع"</string>
<string name="global_action_logout" msgid="935179188218826050">"سیشن ختم کریں"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="bugreport_title" msgid="2667494803742548533">"بگ کی اطلاع لیں"</string>
<string name="bugreport_message" msgid="398447048750350456">"ایک ای میل پیغام کے بطور بھیجنے کیلئے، یہ آپ کے موجودہ آلہ کی حالت کے بارے میں معلومات جمع کرے گا۔ بگ کی اطلاع شروع کرنے سے لے کر بھیجنے کیلئے تیار ہونے تک اس میں تھوڑا وقت لگے گا؛ براہ کرم تحمل سے کام لیں۔"</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"متعامل رپورٹ"</string>
@@ -1123,6 +1125,10 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi از خود آن ہو جائے گا"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"جب آپ اعلی معیار کے محفوظ کردہ نیٹ ورک کے قریب ہوں"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"دوبارہ آن نہ کریں"</string>
+ <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) -->
+ <skip />
+ <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) -->
+ <skip />
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Wi-Fi نیٹ ورک میں سائن ان کریں"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"نیٹ ورک میں سائن ان کریں"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1808,4 +1814,11 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"شارٹ کٹ کو بحال نہیں کیا جا سکا، کیونکہ ایپ بیک اپ اور بحالی کو سپورٹ نہیں کرتی ہے"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"ایپ دستخط غیر مماثل ہونے کی وجہ سے شارٹ کٹ کو بحال نہیں کیا جا سکا"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"شارٹ کٹ کو بحال نہیں کیا جا سکا"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"شارٹ کٹ غیر فعال ہے"</string>
+ <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) -->
+ <skip />
+ <!-- no translation found for harmful_app_warning_title (2229996292333310435) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index d7d3941..ce8613f 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -217,6 +217,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Favqulodda chaqiruv"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Nosozlik haqida ma’lumot berish"</string>
<string name="global_action_logout" msgid="935179188218826050">"Seansni yakunlash"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Skrinshot"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Xatoliklar hisoboti"</string>
<string name="bugreport_message" msgid="398447048750350456">"Qurilmangiz holati haqidagi ma’lumotlar to‘planib, e-pochta orqali yuboriladi. Hisobotni tayyorlash biroz vaqt olishi mumkin."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktiv hisobot"</string>
@@ -1123,6 +1124,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi avtomatik ravishda yoqiladi"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Saqlangan tarmoqlar ichidan signali yaxshisi hududida ekaningizda"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Qayta yoqilmasin"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi avtomatik ravishda yoqildi"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Saqlangan tarmoq atrofidasiz: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Wi-Fi tarmoqqa kirish"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Tarmoqqa kirish"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1808,4 +1811,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Ilovada zaxiralash va tiklash ishlamagani uchun yorliq tiklanmadi"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Ilova imzosi mos kelmagani uchun yorliq tiklanmadi"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Yorliq tiklanmadi"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Yorliq faolsizlantirildi"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"O‘chirib tashlash"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Baribir ishga tushirilsin"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"Zararli ilova o‘chirilsinmi?"</string>
</resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index c5a370a..f5a1972 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Không thể khôi phục lối tắt do ứng dụng không hỗ trợ sao lưu và khôi phục"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Không thể khôi phục lối tắt do không khớp chữ ký ứng dụng"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Không thể khôi phục lối tắt"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index e8ad015..70b299e 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"无法恢复快捷方式,因为应用不支持备份和恢复功能"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"无法恢复快捷方式,因为应用签名不相符"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"无法恢复快捷方式"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index bedaef4..3dad724 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"由於應用程式不支援備份和還原功能,因此無法還原捷徑"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"由於應用程式簽署不相符,因此無法還原捷徑"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"無法還原捷徑"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 38109f8..d3ad7b0 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1807,4 +1807,6 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"應用程式不支援備份與還原功能,因此無法還原捷徑"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"應用程式簽署不相符,因此無法還原捷徑"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"無法還原捷徑"</string>
+ <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 57b9fc3..e97f893 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -217,6 +217,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Isimo esiphuthumayo"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Umbiko wephutha"</string>
<string name="global_action_logout" msgid="935179188218826050">"Phothula isikhathi"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Isithombe-skrini"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Thatha umbiko wesiphazamiso"</string>
<string name="bugreport_message" msgid="398447048750350456">"Lokhu kuzoqoqa ulwazi mayelana nesimo samanje sedivayisi yakho, ukuthumela imilayezo ye-imeyili. Kuzothatha isikhathi esincane kusuka ekuqaleni umbiko wesiphazamiso uze ulungele ukuthunyelwa; sicela ubekezele."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Umbiko obandakanyayo"</string>
@@ -1123,6 +1124,8 @@
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"I-Wi-Fi izovuleka ngokuzenzakalela"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Uma useduze kwenethiwekhi yekhwalithi ephezulu elondoloziwe"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ungaphindi uvule"</string>
+ <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"I-Wi‑Fi ivuleke ngokuzenzakalela"</string>
+ <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Useduzane nenethiwekhi elondoloziwe: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Ngena ngemvume kunethiwekhi ye-Wi-Fi"</string>
<string name="network_available_sign_in" msgid="1848877297365446605">"Ngena ngemvume kunethiwekhi"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
@@ -1807,4 +1810,8 @@
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Ayikwazanga ukubuyisa isinqamuleli ngoba uhlelo lokusebenza alusekeli isipele nokubuyisa"</string>
<string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Ayikwazanga ukubuyisa isinqamuleli ngoba isignisha yohlelo lokusebenza ayifani"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Ayikwazanga ukubuyisa isinqamuleli"</string>
+ <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Isinqamuleli sikhutshaziwe"</string>
+ <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Khipha"</string>
+ <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Qalisa noma kunjalo"</string>
+ <string name="harmful_app_warning_title" msgid="2229996292333310435">"Khipha uhlelo lokusebenza oluyingozi?"</string>
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index ee7c795..fd33dc9 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3030,6 +3030,10 @@
value, {@code false}, leaves the screen reader to consider other signals, such as
focusability or the presence of text, to decide what it focus.-->
<attr name="screenReaderFocusable" format="boolean" />
+
+ <!-- The title this view should present to accessibility as a pane title.
+ See {@link android.view.View#setAccessibilityPaneTitle(CharSequence)} -->
+ <attr name="accessibilityPaneTitle" format="string" />
</declare-styleable>
<!-- Attributes that can be assigned to a tag for a particular View. -->
@@ -4735,6 +4739,15 @@
<!-- Extra spacing between lines of text, as a multiplier. The value will not be applied
for the last line of text.-->
<attr name="lineSpacingMultiplier" format="float" />
+ <!-- Explicit height between lines of text. If set, this will override the values set
+ for lineSpacingExtra and lineSpacingMultiplier. -->
+ <attr name="lineHeight" format="dimension" />
+ <!-- Distance from the top of the TextView to the first text baseline. If set, this
+ overrides the value set for paddingTop. -->
+ <attr name="firstBaselineToTopHeight" format="dimension" />
+ <!-- Distance from the bottom of the TextView to the last text baseline. If set, this
+ overrides the value set for paddingBottom. -->
+ <attr name="lastBaselineToBottomHeight" format="dimension" />
<!-- The number of times to repeat the marquee animation. Only applied if the
TextView has marquee enabled. -->
<attr name="marqueeRepeatLimit" format="integer">
@@ -7756,21 +7769,21 @@
<attr name="settingsActivity" />
</declare-styleable>
- <!-- @SystemApi Use <code>trust-agent</code> as the root tag of the XML resource that
+ <!-- Use <code>trust-agent</code> as the root tag of the XML resource that
describes an {@link android.service.trust.TrustAgentService}, which is
referenced from its {@link android.service.trust.TrustAgentService#TRUST_AGENT_META_DATA}
meta-data entry. Described here are the attributes that can be included in that tag.
@hide -->
<declare-styleable name="TrustAgent">
- <!-- @SystemApi Component name of an activity that allows the user to modify
+ <!-- Component name of an activity that allows the user to modify
the settings for this trust agent. @hide -->
<attr name="settingsActivity" />
- <!-- @SystemApi Title for a preference that allows that user to launch the
+ <!-- Title for a preference that allows that user to launch the
activity to modify trust agent settings. @hide -->
<attr name="title" />
- <!-- @SystemApi Summary for the same preference as the title. @hide -->
+ <!-- Summary for the same preference as the title. @hide -->
<attr name="summary" />
- <!-- @SystemApi Whether trust agent can unlock a user profile @hide -->
+ <!-- Whether trust agent can unlock a user profile @hide -->
<attr name="unlockProfile" format="boolean"/>
</declare-styleable>
@@ -7980,16 +7993,16 @@
by the enrollment application.
Described here are the attributes that can be included in that tag.
@hide
- @SystemApi -->
+ -->
<declare-styleable name="VoiceEnrollmentApplication">
- <!-- A globally unique ID for the keyphrase. @hide @SystemApi -->
+ <!-- A globally unique ID for the keyphrase. @hide -->
<attr name="searchKeyphraseId" format="integer" />
- <!-- The actual keyphrase/hint text, or empty if not keyphrase dependent. @hide @SystemApi -->
+ <!-- The actual keyphrase/hint text, or empty if not keyphrase dependent. @hide -->
<attr name="searchKeyphrase" format="string" />
<!-- A comma separated list of BCP-47 language tag for locales that are supported
- for this keyphrase, or empty if not locale dependent. @hide @SystemApi -->
+ for this keyphrase, or empty if not locale dependent. @hide -->
<attr name="searchKeyphraseSupportedLocales" format="string" />
- <!-- Flags for supported recognition modes. @hide @SystemApi -->
+ <!-- Flags for supported recognition modes. @hide -->
<attr name="searchKeyphraseRecognitionFlags">
<flag name="none" value="0" />
<flag name="voiceTrigger" value="0x1" />
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index efbe9c2..287f296 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2568,9 +2568,9 @@
e.g. name=ro.oem.sku value=MKT210.
Overlay will be ignored unless system property exists and is
set to specified value -->
- <!-- @hide @SystemApi This shouldn't be public. -->
+ <!-- @hide This shouldn't be public. -->
<attr name="requiredSystemPropertyName" format="string" />
- <!-- @hide @SystemApi This shouldn't be public. -->
+ <!-- @hide This shouldn't be public. -->
<attr name="requiredSystemPropertyValue" format="string" />
</declare-styleable>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index fd78500..a078d8b 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -73,7 +73,7 @@
<drawable name="editbox_dropdown_light_frame">@drawable/editbox_dropdown_background</drawable>
<drawable name="input_method_fullscreen_background">#fff9f9f9</drawable>
- <color name="input_method_navigation_guard">#ff000000</color>
+ <color name="decor_view_status_guard">#ff000000</color>
<!-- For date picker widget -->
<drawable name="selected_day_background">#ff0092f4</drawable>
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index 0413100..f8a77f8 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -77,9 +77,9 @@
<item name="secondary_content_alpha_material_dark" format="float" type="dimen">.7</item>
<item name="secondary_content_alpha_material_light" format="float" type="dimen">0.54</item>
- <item name="highlight_alpha_material_light" format="float" type="dimen">0.12</item>
- <item name="highlight_alpha_material_dark" format="float" type="dimen">0.20</item>
- <item name="highlight_alpha_material_colored" format="float" type="dimen">0.26</item>
+ <item name="highlight_alpha_material_light" format="float" type="dimen">0.16</item>
+ <item name="highlight_alpha_material_dark" format="float" type="dimen">0.32</item>
+ <item name="highlight_alpha_material_colored" format="float" type="dimen">0.48</item>
<!-- Primary & accent colors -->
<eat-comment />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d2685cf..3b02a96 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2481,6 +2481,7 @@
<string-array translatable="false" name="config_globalActionsList">
<item>power</item>
<item>restart</item>
+ <item>screenshot</item>
<item>logout</item>
<item>bugreport</item>
<item>users</item>
@@ -2667,6 +2668,13 @@
<bool name="config_sms_force_7bit_encoding">false</bool>
+ <!-- Number of physical SIM slots on the device. This includes both eSIM and pSIM slots, and
+ is not necessarily the same as the number of phones/logical modems supported by the device.
+ For example, a multi-sim device can have 2 phones/logical modems, but 3 physical slots,
+ or a single SIM device can have 1 phones/logical modems, but 2 physical slots (one eSIM
+ and one pSIM) -->
+ <integer name="config_num_physical_slots">1</integer>
+
<!--Thresholds for LTE dbm in status bar-->
<integer-array translatable="false" name="config_lteDbmThresholds">
<item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
@@ -2769,6 +2777,18 @@
some existing device-specific resource overlays. -->
<bool name="config_mainBuiltInDisplayIsRound">@bool/config_windowIsRound</bool>
+ <!-- The bounding path of the cutout region of the main built-in display.
+ Must either be empty if there is no cutout region, or a string that is parsable by
+ {@link android.util.PathParser}.
+ The path is assumed to be specified in display coordinates with pixel units and in
+ the display's native orientation. -->
+ <string translatable="false" name="config_mainBuiltInDisplayCutout"></string>
+
+ <!-- Whether the display cutout region of the main built-in display should be forced to
+ black in software (to avoid aliasing or emulate a cutout that is not physically existent).
+ -->
+ <bool name="config_fillMainBuiltInDisplayCutout">false</bool>
+
<!-- Ultrasound support for Mic/speaker path -->
<!-- Whether the default microphone audio source supports near-ultrasound frequencies
(range of 18 - 21 kHz). -->
@@ -3229,4 +3249,7 @@
<string name="config_fontFamilyButton">@string/font_family_button_material</string>
<string translatable="false" name="config_batterySaverDeviceSpecificConfig"></string>
+
+ <!-- Package name that should be granted Notification Assistant access -->
+ <string name="config_defaultAssistantAccessPackage" translatable="false">android.ext.services</string>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 6c1661c..2c824ea0 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -152,11 +152,11 @@
<dimen name="dialog_padding">16dp</dimen>
<!-- The margin on the start of the content view -->
- <dimen name="notification_content_margin_start">24dp</dimen>
+ <dimen name="notification_content_margin_start">16dp</dimen>
<!-- The margin on the end of the content view
Keep in sync with notification_content_plus_picture_margin! -->
- <dimen name="notification_content_margin_end">24dp</dimen>
+ <dimen name="notification_content_margin_end">16dp</dimen>
<!-- The margin on the end of the content view with a picture.
Keep in sync with notification_content_plus_picture_margin! -->
@@ -166,7 +166,7 @@
content end margin.
Keep equal to (notification_content_picture_margin + notification_content_margin_end)!
-->
- <dimen name="notification_content_plus_picture_margin_end">80dp</dimen>
+ <dimen name="notification_content_plus_picture_margin_end">72dp</dimen>
<!-- The additional margin on the sides of the ambient view. -->
<dimen name="notification_extra_margin_ambient">16dp</dimen>
@@ -175,10 +175,10 @@
<dimen name="notification_action_list_height">56dp</dimen>
<!-- height of the content margin to accomodate for the header -->
- <dimen name="notification_content_margin_top">41.5dp</dimen>
+ <dimen name="notification_content_margin_top">46dp</dimen>
<!-- height of the content margin on the bottom -->
- <dimen name="notification_content_margin_bottom">18dp</dimen>
+ <dimen name="notification_content_margin_bottom">20dp</dimen>
<!-- The height of the progress bar. -->
<dimen name="notification_progress_bar_height">15dp</dimen>
@@ -187,19 +187,19 @@
<dimen name="notification_progress_margin_top">8dp</dimen>
<!-- height of the notification header (for icon and package name) -->
- <dimen name="notification_header_height">54dp</dimen>
+ <dimen name="notification_header_height">50dp</dimen>
<!-- The height of the background for a notification header on a group -->
<dimen name="notification_header_background_height">49.5dp</dimen>
<!-- The top padding for the notification header -->
- <dimen name="notification_header_padding_top">14dp</dimen>
+ <dimen name="notification_header_padding_top">16dp</dimen>
<!-- The bottom padding for the notification header -->
- <dimen name="notification_header_padding_bottom">13dp</dimen>
+ <dimen name="notification_header_padding_bottom">16dp</dimen>
<!-- The margin at the bottom of the notification header. -->
- <dimen name="notification_header_margin_bottom">5dp</dimen>
+ <dimen name="notification_header_margin_bottom">0dp</dimen>
<!-- The end margin after the application icon in the notification header -->
<dimen name="notification_header_icon_margin_end">3dp</dimen>
@@ -250,7 +250,7 @@
<dimen name="media_notification_expanded_image_margin_bottom">20dp</dimen>
<!-- The absolute height for the header in a media notification. -->
- <dimen name="media_notification_header_height">53dp</dimen>
+ <dimen name="media_notification_header_height">@dimen/notification_header_height</dimen>
<!-- The margin of the content to an image-->
<dimen name="notification_content_image_margin_end">8dp</dimen>
@@ -590,7 +590,7 @@
<!-- The maximum width of a image in a media notification. The images will be reduced to that width in case they are bigger.-->
<dimen name="notification_media_image_max_width">280dp</dimen>
<!-- The size of the right icon -->
- <dimen name="notification_right_icon_size">40dp</dimen>
+ <dimen name="notification_right_icon_size">38dp</dimen>
<!-- The maximum height of any image in a remote view. This is applied to all images in custom remoteviews. -->
<dimen name="notification_custom_view_max_image_height_low_ram">208dp</dimen>
@@ -607,7 +607,7 @@
<!-- The size of the right icon image when on low ram -->
<dimen name="notification_right_icon_size_low_ram">40dp</dimen>
- <dimen name="messaging_avatar_size">24dp</dimen>
+ <dimen name="messaging_avatar_size">@dimen/notification_right_icon_size</dimen>
<!-- Max width/height of the autofill data set picker as a fraction of the screen width/height -->
<dimen name="autofill_dataset_picker_max_size">90%</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 6ec88dc..656add6 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2861,6 +2861,10 @@
<public name="widgetFeatures" />
<public name="appComponentFactory" />
<public name="fallbackLineSpacing" />
+ <public name="accessibilityPaneTitle" />
+ <public name="firstBaselineToTopHeight" />
+ <public name="lastBaselineToBottomHeight" />
+ <public name="lineHeight" />
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0618a820..b2fa294 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -484,6 +484,9 @@
<!-- label for item that logouts the current user -->
<string name="global_action_logout">End session</string>
+ <!-- label for screenshot item in power menu -->
+ <string name="global_action_screenshot">Screenshot</string>
+
<!-- Take bug report menu title [CHAR LIMIT=NONE] -->
<string name="bugreport_title">Take bug report</string>
<!-- Message in bugreport dialog describing what it does [CHAR LIMIT=NONE] -->
@@ -3030,6 +3033,10 @@
<string name="wifi_wakeup_onboarding_subtext">When you\'re near a high quality saved network</string>
<!--Notification action to disable Wi-Fi Wake during onboarding.-->
<string name="wifi_wakeup_onboarding_action_disable">Don\'t turn back on</string>
+ <!--Notification title for when Wi-Fi Wake enables Wi-Fi.-->
+ <string name="wifi_wakeup_enabled_title">Wi\u2011Fi turned on automatically</string>
+ <!--Notification content for when Wi-Fi Wake enables Wi-Fi. %1$s is the SSID of the nearby saved network that triggered the wakeup. -->
+ <string name="wifi_wakeup_enabled_content">You\u0027re near a saved network: <xliff:g id="network_ssid">%1$s</xliff:g></string>
<!-- A notification is shown when a wifi captive portal network is detected. This is the notification's title. -->
<string name="wifi_available_sign_in">Sign in to Wi-Fi network</string>
@@ -4795,4 +4802,11 @@
<!--Battery saver warning. STOPSHIP: Remove it eventually. -->
<string name="battery_saver_warning_title" translatable="false">Extreme battery saver</string>
+
+ <!-- Label for the uninstall button on the harmful app warning dialog. -->
+ <string name="harmful_app_warning_uninstall">Uninstall</string>
+ <!-- Label for the launch anyway button on the harmful app warning dialog. -->
+ <string name="harmful_app_warning_launch_anyway">Launch anyway</string>
+ <!-- Title for the harmful app warning dialog. -->
+ <string name="harmful_app_warning_title">Uninstall harmful app?</string>
</resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index def650a..fa3cf2f 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -502,20 +502,17 @@
<style name="Widget.Material.Notification.ProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal" />
<style name="Widget.Material.Notification.MessagingText" parent="Widget.Material.Light.TextView">
- <item name="layout_width">match_parent</item>
+ <item name="layout_width">wrap_content</item>
<item name="layout_height">wrap_content</item>
<item name="ellipsize">end</item>
<item name="textAppearance">@style/TextAppearance.Material.Notification</item>
- <item name="background">@drawable/messaging_message_background</item>
</style>
<style name="Widget.Material.Notification.MessagingName" parent="Widget.Material.Light.TextView">
<item name="layout_width">wrap_content</item>
<item name="layout_height">wrap_content</item>
<item name="ellipsize">end</item>
- <item name="textAppearance">@style/TextAppearance.Material.Notification</item>
- <item name="textColor">@color/notification_primary_text_color_light</item>
- <item name="textSize">12sp</item>
+ <item name="textAppearance">@style/TextAppearance.Material.Notification.Title</item>
</style>
<!-- Widget Styles -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 22a4251..6e33648 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -462,6 +462,7 @@
<java-symbol type="bool" name="config_useDefaultFocusHighlight" />
<java-symbol type="array" name="config_deviceSpecificSystemServices" />
<java-symbol type="string" name="config_deviceSpecificDevicePolicyManagerService" />
+ <java-symbol type="integer" name="config_num_physical_slots" />
<java-symbol type="color" name="tab_indicator_text_v4" />
@@ -1721,6 +1722,7 @@
<java-symbol type="string" name="global_action_lockdown" />
<java-symbol type="string" name="global_action_voice_assist" />
<java-symbol type="string" name="global_action_assist" />
+ <java-symbol type="string" name="global_action_screenshot" />
<java-symbol type="string" name="invalidPuk" />
<java-symbol type="string" name="lockscreen_carrier_default" />
<java-symbol type="style" name="Animation.LockScreen" />
@@ -1815,7 +1817,7 @@
<java-symbol type="bool" name="config_wimaxEnabled" />
<java-symbol type="bool" name="show_ongoing_ime_switcher" />
<java-symbol type="color" name="config_defaultNotificationColor" />
- <java-symbol type="color" name="input_method_navigation_guard" />
+ <java-symbol type="color" name="decor_view_status_guard" />
<java-symbol type="drawable" name="ic_notification_ime_default" />
<java-symbol type="drawable" name="ic_menu_refresh" />
<java-symbol type="drawable" name="ic_settings" />
@@ -1918,6 +1920,8 @@
<java-symbol type="string" name="wifi_wakeup_onboarding_title" />
<java-symbol type="string" name="wifi_wakeup_onboarding_subtext" />
<java-symbol type="string" name="wifi_wakeup_onboarding_action_disable" />
+ <java-symbol type="string" name="wifi_wakeup_enabled_title" />
+ <java-symbol type="string" name="wifi_wakeup_enabled_content" />
<java-symbol type="string" name="accessibility_binding_label" />
<java-symbol type="string" name="adb_active_notification_message" />
<java-symbol type="string" name="adb_active_notification_title" />
@@ -2434,9 +2438,6 @@
<!-- Cascading submenus -->
<java-symbol type="dimen" name="cascading_menus_min_smallest_width" />
- <!-- From SignalStrength -->
- <java-symbol type="array" name="config_lteDbmThresholds" />
-
<java-symbol type="string" name="android_system_label" />
<java-symbol type="string" name="system_error_wipe_data" />
<java-symbol type="string" name="system_error_manufacturer" />
@@ -2549,6 +2550,7 @@
<java-symbol type="bool" name="config_mainBuiltInDisplayIsRound" />
<java-symbol type="id" name="actions_container" />
+ <java-symbol type="id" name="smart_reply_container" />
<java-symbol type="id" name="remote_input_tag" />
<java-symbol type="attr" name="seekBarDialogPreferenceStyle" />
@@ -2608,10 +2610,12 @@
<java-symbol type="string" name="notification_hidden_text" />
<java-symbol type="id" name="app_name_text" />
<java-symbol type="id" name="header_text" />
+ <java-symbol type="id" name="header_text_secondary" />
<java-symbol type="id" name="expand_button" />
<java-symbol type="id" name="notification_header" />
<java-symbol type="id" name="time_divider" />
<java-symbol type="id" name="header_text_divider" />
+ <java-symbol type="id" name="header_text_secondary_divider" />
<java-symbol type="id" name="text_line_1" />
<java-symbol type="drawable" name="ic_expand_notification" />
<java-symbol type="drawable" name="ic_collapse_notification" />
@@ -2887,8 +2891,9 @@
<java-symbol type="bool" name="config_permissionReviewRequired" />
-
+ <!-- Global actions icons -->
<java-symbol type="drawable" name="ic_restart" />
+ <java-symbol type="drawable" name="ic_screenshot" />
<java-symbol type="drawable" name="emergency_icon" />
@@ -3198,6 +3203,9 @@
<java-symbol type="string" name="battery_saver_warning_title" />
<java-symbol type="string" name="global_action_logout" />
+ <java-symbol type="string" name="config_mainBuiltInDisplayCutout" />
+ <java-symbol type="drawable" name="messaging_user" />
+ <java-symbol type="bool" name="config_fillMainBuiltInDisplayCutout" />
<java-symbol type="drawable" name="ic_logout" />
<java-symbol type="array" name="config_autoBrightnessDisplayValuesNits" />
@@ -3205,4 +3213,10 @@
<java-symbol type="array" name="config_screenBrightnessNits" />
<java-symbol type="string" name="shortcut_disabled_reason_unknown" />
+
+ <java-symbol type="string" name="harmful_app_warning_uninstall" />
+ <java-symbol type="string" name="harmful_app_warning_launch_anyway" />
+ <java-symbol type="string" name="harmful_app_warning_title" />
+
+ <java-symbol type="string" name="config_defaultAssistantAccessPackage" />
</resources>
diff --git a/core/tests/coretests/res/layout/activity_text_view.xml b/core/tests/coretests/res/layout/activity_text_view.xml
index e795c10..dca1656 100644
--- a/core/tests/coretests/res/layout/activity_text_view.xml
+++ b/core/tests/coretests/res/layout/activity_text_view.xml
@@ -25,4 +25,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/nonselectable_textview"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
</LinearLayout>
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index b51c677..9ab7544 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -18,11 +18,13 @@
import static com.android.internal.util.NotificationColorUtil.satisfiesTextContrast;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
@@ -40,6 +42,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.function.Consumer;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class NotificationTest {
@@ -281,6 +285,40 @@
assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
}
+ @Test
+ public void action_builder_hasDefault() {
+ Notification.Action action = makeNotificationAction(null);
+ assertEquals(Notification.Action.SEMANTIC_ACTION_NONE, action.getSemanticAction());
+ }
+
+ @Test
+ public void action_builder_setSemanticAction() {
+ Notification.Action action = makeNotificationAction(
+ builder -> builder.setSemanticAction(Notification.Action.SEMANTIC_ACTION_REPLY));
+ assertEquals(Notification.Action.SEMANTIC_ACTION_REPLY, action.getSemanticAction());
+ }
+
+ @Test
+ public void action_parcel() {
+ Notification.Action action = writeAndReadParcelable(
+ makeNotificationAction(builder -> {
+ builder.setSemanticAction(Notification.Action.SEMANTIC_ACTION_ARCHIVE);
+ builder.setAllowGeneratedReplies(true);
+ }));
+
+ assertEquals(Notification.Action.SEMANTIC_ACTION_ARCHIVE, action.getSemanticAction());
+ assertTrue(action.getAllowGeneratedReplies());
+ }
+
+ @Test
+ public void action_clone() {
+ Notification.Action action = makeNotificationAction(
+ builder -> builder.setSemanticAction(Notification.Action.SEMANTIC_ACTION_DELETE));
+ assertEquals(
+ Notification.Action.SEMANTIC_ACTION_DELETE,
+ action.clone().getSemanticAction());
+ }
+
private Notification.Builder getMediaNotification() {
MediaSession session = new MediaSession(mContext, "test");
return new Notification.Builder(mContext, "color")
@@ -300,4 +338,18 @@
p.setDataPosition(0);
return p.readParcelable(/* classLoader */ null);
}
+
+ /**
+ * Creates a Notification.Action by mocking initial dependencies and then applying
+ * transformations if they're defined.
+ */
+ private Notification.Action makeNotificationAction(
+ @Nullable Consumer<Notification.Action.Builder> transformation) {
+ Notification.Action.Builder actionBuilder =
+ new Notification.Action.Builder(null, "Test Title", null);
+ if (transformation != null) {
+ transformation.accept(actionBuilder);
+ }
+ return actionBuilder.build();
+ }
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index c19a343..aefc47e 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -42,8 +42,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
-// TODO: b/70616950
-//@Presubmit
+@Presubmit
public class ObjectPoolTests {
// 1. Check if two obtained objects from pool are not the same.
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
index 53f4f3a..267267e 100644
--- a/core/tests/coretests/src/android/content/pm/PackageParserTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
@@ -146,14 +146,14 @@
}
private void verifyComputeTargetSdkVersion(int targetSdkVersion, String targetSdkCodename,
- boolean isPlatformReleased, int expectedTargetSdk) {
+ boolean isPlatformReleased, int expectedTargetSdk, boolean forceCurrentDev) {
final String[] outError = new String[1];
final int result = PackageParser.computeTargetSdkVersion(
targetSdkVersion,
targetSdkCodename,
- PLATFORM_VERSION,
isPlatformReleased ? CODENAMES_RELEASED : CODENAMES_PRE_RELEASE,
- outError);
+ outError,
+ forceCurrentDev);
assertEquals(result, expectedTargetSdk);
@@ -169,34 +169,45 @@
// Do allow older release targetSdkVersion on pre-release platform.
// APP: Released API 10
// DEV: Pre-release API 20
- verifyComputeTargetSdkVersion(OLDER_VERSION, RELEASED, false, OLDER_VERSION);
+ verifyComputeTargetSdkVersion(OLDER_VERSION, RELEASED, false, OLDER_VERSION,
+ false /* forceCurrentDev */);
// Do allow same release targetSdkVersion on pre-release platform.
// APP: Released API 20
// DEV: Pre-release API 20
- verifyComputeTargetSdkVersion(PLATFORM_VERSION, RELEASED, false, PLATFORM_VERSION);
+ verifyComputeTargetSdkVersion(PLATFORM_VERSION, RELEASED, false, PLATFORM_VERSION,
+ false /* forceCurrentDev */);
// Do allow newer release targetSdkVersion on pre-release platform.
// APP: Released API 30
// DEV: Pre-release API 20
- verifyComputeTargetSdkVersion(NEWER_VERSION, RELEASED, false, NEWER_VERSION);
+ verifyComputeTargetSdkVersion(NEWER_VERSION, RELEASED, false, NEWER_VERSION,
+ false /* forceCurrentDev */);
// Don't allow older pre-release targetSdkVersion on pre-release platform.
// APP: Pre-release API 10
// DEV: Pre-release API 20
- verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, false, -1);
+ verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, false, -1,
+ false /* forceCurrentDev */);
// Do allow same pre-release targetSdkVersion on pre-release platform,
// but overwrite the specified version with CUR_DEVELOPMENT.
// APP: Pre-release API 20
// DEV: Pre-release API 20
verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, false,
- Build.VERSION_CODES.CUR_DEVELOPMENT);
+ Build.VERSION_CODES.CUR_DEVELOPMENT, false /* forceCurrentDev */);
// Don't allow newer pre-release targetSdkVersion on pre-release platform.
// APP: Pre-release API 30
// DEV: Pre-release API 20
- verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false, -1);
+ verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false, -1,
+ false /* forceCurrentDev */);
+
+ // Force newer pre-release targetSdkVersion to current pre-release platform.
+ // APP: Pre-release API 30
+ // DEV: Pre-release API 20
+ verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false,
+ Build.VERSION_CODES.CUR_DEVELOPMENT, true /* forceCurrentDev */);
}
@Test
@@ -204,32 +215,38 @@
// Do allow older release targetSdkVersion on released platform.
// APP: Released API 10
// DEV: Released API 20
- verifyComputeTargetSdkVersion(OLDER_VERSION, RELEASED, true, OLDER_VERSION);
+ verifyComputeTargetSdkVersion(OLDER_VERSION, RELEASED, true, OLDER_VERSION,
+ false /* forceCurrentDev */);
// Do allow same release targetSdkVersion on released platform.
// APP: Released API 20
// DEV: Released API 20
- verifyComputeTargetSdkVersion(PLATFORM_VERSION, RELEASED, true, PLATFORM_VERSION);
+ verifyComputeTargetSdkVersion(PLATFORM_VERSION, RELEASED, true, PLATFORM_VERSION,
+ false /* forceCurrentDev */);
// Do allow newer release targetSdkVersion on released platform.
// APP: Released API 30
// DEV: Released API 20
- verifyComputeTargetSdkVersion(NEWER_VERSION, RELEASED, true, NEWER_VERSION);
+ verifyComputeTargetSdkVersion(NEWER_VERSION, RELEASED, true, NEWER_VERSION,
+ false /* forceCurrentDev */);
// Don't allow older pre-release targetSdkVersion on released platform.
// APP: Pre-release API 10
// DEV: Released API 20
- verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, true, -1);
+ verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, true, -1,
+ false /* forceCurrentDev */);
// Don't allow same pre-release targetSdkVersion on released platform.
// APP: Pre-release API 20
// DEV: Released API 20
- verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, true, -1);
+ verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, true, -1,
+ false /* forceCurrentDev */);
// Don't allow newer pre-release targetSdkVersion on released platform.
// APP: Pre-release API 30
// DEV: Released API 20
- verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, -1);
+ verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, -1,
+ false /* forceCurrentDev */);
}
/**
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 7cfedc8..d5689c7 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -204,6 +204,7 @@
Settings.Global.ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE,
Settings.Global.ENABLE_DISKSTATS_LOGGING,
Settings.Global.ENABLE_EPHEMERAL_FEATURE,
+ Settings.Global.ENABLE_SMART_REPLIES_IN_NOTIFICATIONS,
Settings.Global.ENHANCED_4G_MODE_ENABLED,
Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES,
Settings.Global.ERROR_LOGCAT_PREFIX,
@@ -211,6 +212,8 @@
Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS,
Settings.Global.FANCY_IME_ANIMATIONS,
Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
+ Settings.Global.FORCED_APP_STANDBY_ENABLED,
+ Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
Settings.Global.FSTRIM_MANDATORY_INTERVAL,
Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
Settings.Global.GLOBAL_HTTP_PROXY_HOST,
@@ -349,6 +352,7 @@
Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES,
Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE,
+ Settings.Global.SYS_VDSO,
Settings.Global.TCP_DEFAULT_INIT_RWND,
Settings.Global.TETHER_DUN_APN,
Settings.Global.TETHER_DUN_REQUIRED,
@@ -420,7 +424,8 @@
Settings.Global.WTF_IS_FATAL,
Settings.Global.ZEN_MODE,
Settings.Global.ZEN_MODE_CONFIG_ETAG,
- Settings.Global.ZEN_MODE_RINGER_LEVEL);
+ Settings.Global.ZEN_MODE_RINGER_LEVEL,
+ Settings.Global.ZRAM_ENABLED);
private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
newHashSet(
@@ -512,6 +517,7 @@
Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
Settings.Secure.SETTINGS_CLASSNAME,
Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, // candidate?
+ Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
Settings.Secure.SKIP_FIRST_USE_HINTS, // candidate?
Settings.Secure.SMS_DEFAULT_APPLICATION,
Settings.Secure.TRUST_AGENTS_INITIALIZED,
diff --git a/core/tests/coretests/src/android/text/MeasuredTextTest.java b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
similarity index 87%
rename from core/tests/coretests/src/android/text/MeasuredTextTest.java
rename to core/tests/coretests/src/android/text/MeasuredParagraphTest.java
index ddef0c6..5d33397 100644
--- a/core/tests/coretests/src/android/text/MeasuredTextTest.java
+++ b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
@@ -31,7 +31,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class MeasuredTextTest {
+public class MeasuredParagraphTest {
private static final TextDirectionHeuristic LTR = TextDirectionHeuristics.LTR;
private static final TextDirectionHeuristic RTL = TextDirectionHeuristics.RTL;
@@ -60,9 +60,9 @@
@Test
public void buildForBidi() {
- MeasuredText mt = null;
+ MeasuredParagraph mt = null;
- mt = MeasuredText.buildForBidi("XXX", 0, 3, LTR, null);
+ mt = MeasuredParagraph.buildForBidi("XXX", 0, 3, LTR, null);
assertNotNull(mt);
assertNotNull(mt.getChars());
assertEquals("XXX", charsToString(mt.getChars()));
@@ -75,7 +75,7 @@
assertEquals(0, mt.getNativePtr());
// Recycle it
- MeasuredText mt2 = MeasuredText.buildForBidi("_VVV_", 1, 4, RTL, mt);
+ MeasuredParagraph mt2 = MeasuredParagraph.buildForBidi("_VVV_", 1, 4, RTL, mt);
assertEquals(mt2, mt);
assertNotNull(mt2.getChars());
assertEquals("VVV", charsToString(mt.getChars()));
@@ -91,9 +91,9 @@
@Test
public void buildForMeasurement() {
- MeasuredText mt = null;
+ MeasuredParagraph mt = null;
- mt = MeasuredText.buildForMeasurement(PAINT, "XXX", 0, 3, LTR, null);
+ mt = MeasuredParagraph.buildForMeasurement(PAINT, "XXX", 0, 3, LTR, null);
assertNotNull(mt);
assertNotNull(mt.getChars());
assertEquals("XXX", charsToString(mt.getChars()));
@@ -109,7 +109,8 @@
assertEquals(0, mt.getNativePtr());
// Recycle it
- MeasuredText mt2 = MeasuredText.buildForMeasurement(PAINT, "_VVV_", 1, 4, RTL, mt);
+ MeasuredParagraph mt2 =
+ MeasuredParagraph.buildForMeasurement(PAINT, "_VVV_", 1, 4, RTL, mt);
assertEquals(mt2, mt);
assertNotNull(mt2.getChars());
assertEquals("VVV", charsToString(mt.getChars()));
@@ -129,9 +130,9 @@
@Test
public void buildForStaticLayout() {
- MeasuredText mt = null;
+ MeasuredParagraph mt = null;
- mt = MeasuredText.buildForStaticLayout(PAINT, "XXX", 0, 3, LTR, null);
+ mt = MeasuredParagraph.buildForStaticLayout(PAINT, "XXX", 0, 3, LTR, null);
assertNotNull(mt);
assertNotNull(mt.getChars());
assertEquals("XXX", charsToString(mt.getChars()));
@@ -145,7 +146,8 @@
assertNotEquals(0, mt.getNativePtr());
// Recycle it
- MeasuredText mt2 = MeasuredText.buildForStaticLayout(PAINT, "_VVV_", 1, 4, RTL, mt);
+ MeasuredParagraph mt2 =
+ MeasuredParagraph.buildForStaticLayout(PAINT, "_VVV_", 1, 4, RTL, mt);
assertEquals(mt2, mt);
assertNotNull(mt2.getChars());
assertEquals("VVV", charsToString(mt.getChars()));
@@ -163,6 +165,6 @@
@Test
public void testFor70146381() {
- MeasuredText.buildForMeasurement(PAINT, "X…", 0, 2, RTL, null);
+ MeasuredParagraph.buildForMeasurement(PAINT, "X…", 0, 2, RTL, null);
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
new file mode 100644
index 0000000..9ee7fac
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2017 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.textclassifier;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.os.LocaleList;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TextClassificationTest {
+
+ public BitmapDrawable generateTestDrawable(int width, int height, int colorValue) {
+ final int numPixels = width * height;
+ final int[] colors = new int[numPixels];
+ for (int i = 0; i < numPixels; ++i) {
+ colors[i] = colorValue;
+ }
+ final Bitmap bitmap = Bitmap.createBitmap(colors, width, height, Bitmap.Config.ARGB_8888);
+ final BitmapDrawable drawable = new BitmapDrawable(null, bitmap);
+ drawable.setTargetDensity(bitmap.getDensity());
+ return drawable;
+ }
+
+ @Test
+ public void testParcel() {
+ final String text = "text";
+ final BitmapDrawable primaryIcon = generateTestDrawable(16, 16, Color.RED);
+ final String primaryLabel = "primarylabel";
+ final Intent primaryIntent = new Intent("primaryintentaction");
+ final View.OnClickListener primaryOnClick = v -> { };
+ final BitmapDrawable secondaryIcon0 = generateTestDrawable(32, 288, Color.GREEN);
+ final String secondaryLabel0 = "secondarylabel0";
+ final Intent secondaryIntent0 = new Intent("secondaryintentaction0");
+ final BitmapDrawable secondaryIcon1 = generateTestDrawable(576, 288, Color.BLUE);
+ final String secondaryLabel1 = "secondaryLabel1";
+ final Intent secondaryIntent1 = null;
+ final BitmapDrawable secondaryIcon2 = null;
+ final String secondaryLabel2 = null;
+ final Intent secondaryIntent2 = new Intent("secondaryintentaction2");
+ final ColorDrawable secondaryIcon3 = new ColorDrawable(Color.CYAN);
+ final String secondaryLabel3 = null;
+ final Intent secondaryIntent3 = null;
+ final String signature = "signature";
+ final TextClassification reference = new TextClassification.Builder()
+ .setText(text)
+ .setPrimaryAction(primaryIntent, primaryLabel, primaryIcon)
+ .setOnClickListener(primaryOnClick)
+ .addSecondaryAction(null, null, null) // ignored
+ .addSecondaryAction(secondaryIntent0, secondaryLabel0, secondaryIcon0)
+ .addSecondaryAction(secondaryIntent1, secondaryLabel1, secondaryIcon1)
+ .addSecondaryAction(secondaryIntent2, secondaryLabel2, secondaryIcon2)
+ .addSecondaryAction(secondaryIntent3, secondaryLabel3, secondaryIcon3)
+ .setEntityType(TextClassifier.TYPE_ADDRESS, 0.3f)
+ .setEntityType(TextClassifier.TYPE_PHONE, 0.7f)
+ .setSignature(signature)
+ .build();
+
+ // Parcel and unparcel using ParcelableWrapper.
+ final TextClassification.ParcelableWrapper parcelableReference = new TextClassification
+ .ParcelableWrapper(reference);
+ final Parcel parcel = Parcel.obtain();
+ parcelableReference.writeToParcel(parcel, parcelableReference.describeContents());
+ parcel.setDataPosition(0);
+ final TextClassification result =
+ TextClassification.ParcelableWrapper.CREATOR.createFromParcel(
+ parcel).getTextClassification();
+
+ assertEquals(text, result.getText());
+ assertEquals(signature, result.getSignature());
+ assertEquals(4, result.getSecondaryActionsCount());
+
+ // Primary action (re-use existing icon).
+ final Bitmap resPrimaryIcon = ((BitmapDrawable) result.getIcon()).getBitmap();
+ assertEquals(primaryIcon.getBitmap().getPixel(0, 0), resPrimaryIcon.getPixel(0, 0));
+ assertEquals(16, resPrimaryIcon.getWidth());
+ assertEquals(16, resPrimaryIcon.getHeight());
+ assertEquals(primaryLabel, result.getLabel());
+ assertEquals(primaryIntent.getAction(), result.getIntent().getAction());
+ assertEquals(null, result.getOnClickListener()); // Non-parcelable.
+
+ // Secondary action 0 (scale with height limit).
+ final Bitmap resSecondaryIcon0 = ((BitmapDrawable) result.getSecondaryIcon(0)).getBitmap();
+ assertEquals(secondaryIcon0.getBitmap().getPixel(0, 0), resSecondaryIcon0.getPixel(0, 0));
+ assertEquals(16, resSecondaryIcon0.getWidth());
+ assertEquals(144, resSecondaryIcon0.getHeight());
+ assertEquals(secondaryLabel0, result.getSecondaryLabel(0));
+ assertEquals(secondaryIntent0.getAction(), result.getSecondaryIntent(0).getAction());
+
+ // Secondary action 1 (scale with width limit).
+ final Bitmap resSecondaryIcon1 = ((BitmapDrawable) result.getSecondaryIcon(1)).getBitmap();
+ assertEquals(secondaryIcon1.getBitmap().getPixel(0, 0), resSecondaryIcon1.getPixel(0, 0));
+ assertEquals(144, resSecondaryIcon1.getWidth());
+ assertEquals(72, resSecondaryIcon1.getHeight());
+ assertEquals(secondaryLabel1, result.getSecondaryLabel(1));
+ assertEquals(null, result.getSecondaryIntent(1));
+
+ // Secondary action 2 (no icon).
+ assertEquals(null, result.getSecondaryIcon(2));
+ assertEquals(null, result.getSecondaryLabel(2));
+ assertEquals(secondaryIntent2.getAction(), result.getSecondaryIntent(2).getAction());
+
+ // Secondary action 3 (convert non-bitmap drawable with negative size).
+ final Bitmap resSecondaryIcon3 = ((BitmapDrawable) result.getSecondaryIcon(3)).getBitmap();
+ assertEquals(secondaryIcon3.getColor(), resSecondaryIcon3.getPixel(0, 0));
+ assertEquals(1, resSecondaryIcon3.getWidth());
+ assertEquals(1, resSecondaryIcon3.getHeight());
+ assertEquals(null, result.getSecondaryLabel(3));
+ assertEquals(null, result.getSecondaryIntent(3));
+
+ // Entities.
+ assertEquals(2, result.getEntityCount());
+ assertEquals(TextClassifier.TYPE_PHONE, result.getEntity(0));
+ assertEquals(TextClassifier.TYPE_ADDRESS, result.getEntity(1));
+ assertEquals(0.7f, result.getConfidenceScore(TextClassifier.TYPE_PHONE), 1e-7f);
+ assertEquals(0.3f, result.getConfidenceScore(TextClassifier.TYPE_ADDRESS), 1e-7f);
+ }
+
+ @Test
+ public void testParcelOptions() {
+ TextClassification.Options reference = new TextClassification.Options();
+ reference.setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY));
+
+ // Parcel and unparcel.
+ final Parcel parcel = Parcel.obtain();
+ reference.writeToParcel(parcel, reference.describeContents());
+ parcel.setDataPosition(0);
+ TextClassification.Options result = TextClassification.Options.CREATOR.createFromParcel(
+ parcel);
+
+ assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags());
+ }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
new file mode 100644
index 0000000..a82542c
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2017 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.textclassifier;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.LocaleList;
+import android.os.Parcel;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.ArrayMap;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TextLinksTest {
+
+ private TextClassificationManager mTcm;
+ private TextClassifier mClassifier;
+
+ @Before
+ public void setup() {
+ mTcm = InstrumentationRegistry.getTargetContext()
+ .getSystemService(TextClassificationManager.class);
+ mTcm.setTextClassifier(null);
+ mClassifier = mTcm.getTextClassifier();
+ }
+
+ private Map<String, Float> getEntityScores(float address, float phone, float other) {
+ final Map<String, Float> result = new ArrayMap<>();
+ if (address > 0.f) {
+ result.put(TextClassifier.TYPE_ADDRESS, address);
+ }
+ if (phone > 0.f) {
+ result.put(TextClassifier.TYPE_PHONE, phone);
+ }
+ if (other > 0.f) {
+ result.put(TextClassifier.TYPE_OTHER, other);
+ }
+ return result;
+ }
+
+ @Test
+ public void testParcel() {
+ final String fullText = "this is just a test";
+ final TextLinks reference = new TextLinks.Builder(fullText)
+ .addLink(new TextLinks.TextLink(fullText, 0, 4, getEntityScores(0.f, 0.f, 1.f)))
+ .addLink(new TextLinks.TextLink(fullText, 5, 12, getEntityScores(.8f, .1f, .5f)))
+ .build();
+
+ // Parcel and unparcel.
+ final Parcel parcel = Parcel.obtain();
+ reference.writeToParcel(parcel, reference.describeContents());
+ parcel.setDataPosition(0);
+ final TextLinks result = TextLinks.CREATOR.createFromParcel(parcel);
+ final List<TextLinks.TextLink> resultList = new ArrayList<>(result.getLinks());
+
+ assertEquals(2, resultList.size());
+ assertEquals(0, resultList.get(0).getStart());
+ assertEquals(4, resultList.get(0).getEnd());
+ assertEquals(1, resultList.get(0).getEntityCount());
+ assertEquals(TextClassifier.TYPE_OTHER, resultList.get(0).getEntity(0));
+ assertEquals(1.f, resultList.get(0).getConfidenceScore(TextClassifier.TYPE_OTHER), 1e-7f);
+ assertEquals(5, resultList.get(1).getStart());
+ assertEquals(12, resultList.get(1).getEnd());
+ assertEquals(3, resultList.get(1).getEntityCount());
+ assertEquals(TextClassifier.TYPE_ADDRESS, resultList.get(1).getEntity(0));
+ assertEquals(TextClassifier.TYPE_OTHER, resultList.get(1).getEntity(1));
+ assertEquals(TextClassifier.TYPE_PHONE, resultList.get(1).getEntity(2));
+ assertEquals(.8f, resultList.get(1).getConfidenceScore(TextClassifier.TYPE_ADDRESS), 1e-7f);
+ assertEquals(.5f, resultList.get(1).getConfidenceScore(TextClassifier.TYPE_OTHER), 1e-7f);
+ assertEquals(.1f, resultList.get(1).getConfidenceScore(TextClassifier.TYPE_PHONE), 1e-7f);
+ }
+
+ @Test
+ public void testParcelOptions() {
+ TextClassifier.EntityConfig entityConfig = new TextClassifier.EntityConfig(
+ TextClassifier.ENTITY_PRESET_NONE);
+ entityConfig.includeEntities("a", "b", "c");
+ entityConfig.excludeEntities("b");
+ TextLinks.Options reference = new TextLinks.Options();
+ reference.setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY));
+ reference.setEntityConfig(entityConfig);
+
+ // Parcel and unparcel.
+ final Parcel parcel = Parcel.obtain();
+ reference.writeToParcel(parcel, reference.describeContents());
+ parcel.setDataPosition(0);
+ TextLinks.Options result = TextLinks.Options.CREATOR.createFromParcel(parcel);
+
+ assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags());
+ assertEquals(Arrays.asList("a", "c"), result.getEntityConfig().getEntities(mClassifier));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
new file mode 100644
index 0000000..e920236
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 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.textclassifier;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.LocaleList;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TextSelectionTest {
+
+ @Test
+ public void testParcel() {
+ final int startIndex = 13;
+ final int endIndex = 37;
+ final String signature = "signature";
+ final TextSelection reference = new TextSelection.Builder(startIndex, endIndex)
+ .setEntityType(TextClassifier.TYPE_ADDRESS, 0.3f)
+ .setEntityType(TextClassifier.TYPE_PHONE, 0.7f)
+ .setEntityType(TextClassifier.TYPE_URL, 0.1f)
+ .setSignature(signature)
+ .build();
+
+ // Parcel and unparcel using ParcelableWrapper.
+ final TextSelection.ParcelableWrapper parcelableReference = new TextSelection
+ .ParcelableWrapper(reference);
+ final Parcel parcel = Parcel.obtain();
+ parcelableReference.writeToParcel(parcel, parcelableReference.describeContents());
+ parcel.setDataPosition(0);
+ final TextSelection result =
+ TextSelection.ParcelableWrapper.CREATOR.createFromParcel(
+ parcel).getTextSelection();
+
+ assertEquals(startIndex, result.getSelectionStartIndex());
+ assertEquals(endIndex, result.getSelectionEndIndex());
+ assertEquals(signature, result.getSignature());
+
+ assertEquals(3, result.getEntityCount());
+ assertEquals(TextClassifier.TYPE_PHONE, result.getEntity(0));
+ assertEquals(TextClassifier.TYPE_ADDRESS, result.getEntity(1));
+ assertEquals(TextClassifier.TYPE_URL, result.getEntity(2));
+ assertEquals(0.7f, result.getConfidenceScore(TextClassifier.TYPE_PHONE), 1e-7f);
+ assertEquals(0.3f, result.getConfidenceScore(TextClassifier.TYPE_ADDRESS), 1e-7f);
+ assertEquals(0.1f, result.getConfidenceScore(TextClassifier.TYPE_URL), 1e-7f);
+ }
+
+ @Test
+ public void testParcelOptions() {
+ TextSelection.Options reference = new TextSelection.Options();
+ reference.setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY));
+ reference.setDarkLaunchAllowed(true);
+
+ // Parcel and unparcel.
+ final Parcel parcel = Parcel.obtain();
+ reference.writeToParcel(parcel, reference.describeContents());
+ parcel.setDataPosition(0);
+ TextSelection.Options result = TextSelection.Options.CREATOR.createFromParcel(parcel);
+
+ assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags());
+ assertTrue(result.isDarkLaunchAllowed());
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 1a654f4..bbca12f 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -311,11 +311,20 @@
@Test
public void testToolbarAppearsAfterLinkClicked() throws Throwable {
+ runToolbarAppearsAfterLinkClickedTest(R.id.textview);
+ }
+
+ @Test
+ public void testToolbarAppearsAfterLinkClickedNonselectable() throws Throwable {
+ runToolbarAppearsAfterLinkClickedTest(R.id.nonselectable_textview);
+ }
+
+ private void runToolbarAppearsAfterLinkClickedTest(int id) throws Throwable {
+ TextView textView = mActivity.findViewById(id);
useSystemDefaultTextClassifier();
TextClassificationManager textClassificationManager =
mActivity.getSystemService(TextClassificationManager.class);
TextClassifier textClassifier = textClassificationManager.getTextClassifier();
- final TextView textView = mActivity.findViewById(R.id.textview);
SpannableString content = new SpannableString("Call me at +19148277737");
TextLinks links = textClassifier.generateLinks(content);
links.apply(content, null);
@@ -331,7 +340,7 @@
TextLinks.TextLink textLink = links.getLinks().iterator().next();
int position = (textLink.getStart() + textLink.getEnd()) / 2;
- onView(withId(R.id.textview)).perform(clickOnTextAtIndex(position));
+ onView(withId(id)).perform(clickOnTextAtIndex(position));
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index d2c855b..f169f22 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -168,6 +168,8 @@
<assign-permission name="android.permission.ACCESS_LOWPAN_STATE" uid="lowpan" />
<assign-permission name="android.permission.MANAGE_LOWPAN_INTERFACES" uid="lowpan" />
+ <assign-permission name="android.permission.STATSCOMPANION" uid="statsd" />
+
<!-- This is a list of all the libraries available for application
code to link against. -->
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 4732bec..c0958cd 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -75,6 +75,7 @@
<privapp-permissions package="com.android.launcher3">
<permission name="android.permission.BIND_APPWIDGET"/>
+ <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
<permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
</privapp-permissions>
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 94b219a..d13e05c 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -239,11 +239,12 @@
};
/**
- * Supplied to onException if the provided data is incomplete.
+ * Used if the provided data is incomplete.
*
- * Will never be thrown by ImageDecoder.
+ * May be thrown if there is nothing to display.
*
- * There may be a partial image to display.
+ * If supplied to onPartialImage, there may be a correct partial image to
+ * display.
*/
public static class IncompleteException extends IOException {};
@@ -252,7 +253,7 @@
*
* May be thrown if there is nothing to display.
*
- * If supplied to onException, there may be a correct partial image to
+ * If supplied to onPartialImage, there may be a correct partial image to
* display.
*/
public static class CorruptException extends IOException {};
@@ -275,17 +276,21 @@
/**
* Optional listener supplied to the ImageDecoder.
*/
- public static interface OnExceptionListener {
+ public static interface OnPartialImageListener {
/**
- * Called when there is a problem in the stream or in the data.
- * FIXME: Report how much of the image has been decoded?
+ * Called when there is only a partial image to display.
*
- * @param e IOException containing information about the error.
- * @return True to create and return a {@link Drawable}/
- * {@link Bitmap} with partial data. False to return
- * {@code null}. True is the default.
+ * If the input is incomplete or contains an error, this listener lets
+ * the client know that and allows them to optionally bypass the rest
+ * of the decode/creation process.
+ *
+ * @param e IOException containing information about the error that
+ * interrupted the decode.
+ * @return True (which is the default) to create and return a
+ * {@link Drawable}/{@link Bitmap} with partial data. False to
+ * abort the decode and throw the {@link java.io.IOException}.
*/
- public boolean onException(IOException e);
+ public boolean onPartialImage(IOException e);
};
// Fields
@@ -302,8 +307,8 @@
private boolean mAsAlphaMask = false;
private Rect mCropRect;
- private PostProcess mPostProcess;
- private OnExceptionListener mOnExceptionListener;
+ private PostProcess mPostProcess;
+ private OnPartialImageListener mOnPartialImageListener;
// Objects for interacting with the input.
private InputStream mInputStream;
@@ -557,13 +562,13 @@
}
/**
- * Set (replace) the {@link OnExceptionListener} on this object.
+ * Set (replace) the {@link OnPartialImageListener} on this object.
*
* Will be called if there is an error in the input. Without one, a
* partial {@link Bitmap} will be created.
*/
- public void setOnExceptionListener(OnExceptionListener l) {
- mOnExceptionListener = l;
+ public void setOnPartialImageListener(OnPartialImageListener l) {
+ mOnPartialImageListener = l;
}
/**
@@ -712,7 +717,7 @@
}
Bitmap bm = nDecodeBitmap(decoder.mNativePtr,
- decoder.mOnExceptionListener,
+ decoder.mOnPartialImageListener,
decoder.mPostProcess,
decoder.mDesiredWidth,
decoder.mDesiredHeight,
@@ -722,13 +727,6 @@
false, // mRequireUnpremultiplied
decoder.mPreferRamOverQuality,
decoder.mAsAlphaMask);
- if (bm == null) {
- // FIXME: bm should never be null. Currently a return value
- // of false from onException will result in bm being null. What
- // is the right API to choose to discard partial Bitmaps?
- return null;
- }
-
Resources res = src.getResources();
if (res == null) {
bm.setDensity(Bitmap.DENSITY_NONE);
@@ -786,7 +784,7 @@
decoder.checkState();
return nDecodeBitmap(decoder.mNativePtr,
- decoder.mOnExceptionListener,
+ decoder.mOnPartialImageListener,
decoder.mPostProcess,
decoder.mDesiredWidth,
decoder.mDesiredHeight,
@@ -821,7 +819,7 @@
private static native ImageDecoder nCreate(FileDescriptor fd) throws IOException;
@NonNull
private static native Bitmap nDecodeBitmap(long nativePtr,
- OnExceptionListener listener,
+ OnPartialImageListener listener,
PostProcess postProcess,
int width, int height,
Rect cropRect, boolean mutable,
diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java
index dea194e..4571553 100644
--- a/graphics/java/android/graphics/drawable/RippleBackground.java
+++ b/graphics/java/android/graphics/drawable/RippleBackground.java
@@ -16,17 +16,12 @@
package android.graphics.drawable;
-import android.animation.Animator;
-import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.graphics.Canvas;
-import android.graphics.CanvasProperty;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.FloatProperty;
-import android.view.DisplayListCanvas;
-import android.view.RenderNodeAnimator;
import android.view.animation.LinearInterpolator;
/**
@@ -78,8 +73,8 @@
private void onStateChanged(boolean animateChanged) {
float newOpacity = 0.0f;
- if (mHovered) newOpacity += 1.0f;
- if (mFocused) newOpacity += 1.0f;
+ if (mHovered) newOpacity += .25f;
+ if (mFocused) newOpacity += .75f;
if (mAnimator != null) {
mAnimator.cancel();
mAnimator = null;
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 734cff5..b883656 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -264,8 +264,8 @@
}
setRippleActive(enabled && pressed);
-
setBackgroundActive(hovered, focused);
+
return changed;
}
@@ -879,22 +879,18 @@
// Grab the color for the current state and cut the alpha channel in
// half so that the ripple and background together yield full alpha.
final int color = mState.mColor.getColorForState(getState(), Color.BLACK);
- final int halfAlpha = (Color.alpha(color) / 2) << 24;
final Paint p = mRipplePaint;
if (mMaskColorFilter != null) {
// The ripple timing depends on the paint's alpha value, so we need
// to push just the alpha channel into the paint and let the filter
// handle the full-alpha color.
- final int fullAlphaColor = color | (0xFF << 24);
- mMaskColorFilter.setColor(fullAlphaColor);
-
- p.setColor(halfAlpha);
+ mMaskColorFilter.setColor(color | 0xFF000000);
+ p.setColor(color & 0xFF000000);
p.setColorFilter(mMaskColorFilter);
p.setShader(mMaskShader);
} else {
- final int halfAlphaColor = (color & 0xFFFFFF) | halfAlpha;
- p.setColor(halfAlphaColor);
+ p.setColor(color);
p.setColorFilter(null);
p.setShader(null);
}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index fabcdf0..1690e8c 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -424,15 +424,6 @@
return getmtime(key, UID_SELF);
}
- public boolean duplicate(String srcKey, int srcUid, String destKey, int destUid) {
- try {
- return mBinder.duplicate(srcKey, srcUid, destKey, destUid) == NO_ERROR;
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return false;
- }
- }
-
// TODO: remove this when it's removed from Settings
public boolean isHardwareBacked() {
return isHardwareBacked("RSA");
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 13e1ebe..2e08670 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -765,7 +765,8 @@
for (size_t i = start; i < end; i++) {
glyphs[i - start] = layout.getGlyphId(i);
- float x = hOffset + layout.getX(i);
+ float halfWidth = layout.getCharAdvance(i) * 0.5f;
+ float x = hOffset + layout.getX(i) + halfWidth;
float y = vOffset + layout.getY(i);
SkPoint pos;
@@ -776,8 +777,8 @@
}
xform[i - start].fSCos = tan.x();
xform[i - start].fSSin = tan.y();
- xform[i - start].fTx = pos.x() - tan.y() * y;
- xform[i - start].fTy = pos.y() + tan.x() * y;
+ xform[i - start].fTx = pos.x() - tan.y() * y - halfWidth * tan.x();
+ xform[i - start].fTy = pos.y() + tan.x() * y - halfWidth * tan.y();
}
this->asSkCanvas()->drawTextRSXform(glyphs, sizeof(uint16_t) * N, xform, nullptr, paint);
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanReadback.h b/libs/hwui/pipeline/skia/SkiaVulkanReadback.h
new file mode 100644
index 0000000..65b89d6
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaVulkanReadback.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Readback.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class SkiaVulkanReadback : public Readback {
+public:
+ SkiaVulkanReadback(renderthread::RenderThread& thread) : Readback(thread) {}
+
+ virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect,
+ SkBitmap* bitmap) override {
+ //TODO: implement Vulkan readback.
+ return CopyResult::UnknownError;
+ }
+
+ virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer,
+ SkBitmap* bitmap) override {
+ //TODO: implement Vulkan readback.
+ return CopyResult::UnknownError;
+ }
+};
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 79dc09f..8e0546b 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -24,6 +24,7 @@
#include "hwui/Bitmap.h"
#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/SkiaOpenGLReadback.h"
+#include "pipeline/skia/SkiaVulkanReadback.h"
#include "pipeline/skia/SkiaVulkanPipeline.h"
#include "renderstate/RenderState.h"
#include "renderthread/OpenGLPipeline.h"
@@ -158,12 +159,11 @@
mReadback = new OpenGLReadbackImpl(*this);
break;
case RenderPipelineType::SkiaGL:
- case RenderPipelineType::SkiaVulkan:
- // It works to use the OpenGL pipeline for Vulkan but this is not
- // ideal as it causes us to create an OpenGL context in addition
- // to the Vulkan one.
mReadback = new skiapipeline::SkiaOpenGLReadback(*this);
break;
+ case RenderPipelineType::SkiaVulkan:
+ mReadback = new skiapipeline::SkiaVulkanReadback(*this);
+ break;
default:
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType);
break;
diff --git a/libs/services/Android.bp b/libs/services/Android.bp
new file mode 100644
index 0000000..e5e865f
--- /dev/null
+++ b/libs/services/Android.bp
@@ -0,0 +1,47 @@
+// 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.
+
+// Provides C++ wrappers for system services.
+
+cc_library_shared {
+ name: "libservices",
+ srcs: [
+ ":IDropBoxManagerService.aidl",
+ "src/os/DropBoxManager.cpp",
+ "src/os/StatsLogEventWrapper.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ "libcutils",
+ "libutils",
+ ],
+ header_libs: [
+ "libbase_headers",
+ ],
+ aidl: {
+ include_dirs: ["frameworks/base/core/java/"],
+ },
+
+ export_include_dirs: ["include"],
+ export_header_lib_headers: ["libbase_headers"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
diff --git a/libs/services/Android.mk b/libs/services/Android.mk
deleted file mode 100644
index d72059a..0000000
--- a/libs/services/Android.mk
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright (C) 2010 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-# Provides C++ wrappers for system services.
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libservices
-LOCAL_SRC_FILES := \
- ../../core/java/com/android/internal/os/IDropBoxManagerService.aidl \
- src/os/DropBoxManager.cpp \
- src/os/StatsLogEventWrapper.cpp
-
-LOCAL_AIDL_INCLUDES := \
- $(LOCAL_PATH)/../../core/java
-LOCAL_C_INCLUDES := \
- system/core/include
-LOCAL_SHARED_LIBRARIES := \
- libbinder \
- liblog \
- libcutils \
- libutils
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
-
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-
-include $(BUILD_SHARED_LIBRARY)
-
-
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 306ed83..c6496eb 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -680,6 +680,21 @@
public static final String KEY_LATENCY = "latency";
/**
+ * An optional key describing the maximum number of non-display-order coded frames.
+ * This is an optional parameter that applies only to video encoders. Application should
+ * check the value for this key in the output format to see if codec will produce
+ * non-display-order coded frames. If encoder supports it, the output frames' order will be
+ * different from the display order and each frame's display order could be retrived from
+ * {@link MediaCodec.BufferInfo#presentationTimeUs}. Before API level 27, application may
+ * receive non-display-order coded frames even though the application did not request it.
+ * Note: Application should not rearrange the frames to display order before feeding them
+ * to {@link MediaMuxer#writeSampleData}.
+ * <p>
+ * The default value is 0.
+ */
+ public static final String KEY_OUTPUT_REORDER_DEPTH = "output-reorder-depth";
+
+ /**
* A key describing the desired clockwise rotation on an output surface.
* This key is only used when the codec is configured using an output surface.
* The associated value is an integer, representing degrees. Supported values
diff --git a/media/java/android/media/update/ApiLoader.java b/media/java/android/media/update/ApiLoader.java
index b57e02d..07483f6 100644
--- a/media/java/android/media/update/ApiLoader.java
+++ b/media/java/android/media/update/ApiLoader.java
@@ -49,8 +49,8 @@
Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
sMediaLibrary = libContext.getClassLoader()
.loadClass(UPDATE_CLASS)
- .getMethod(UPDATE_METHOD, Context.class)
- .invoke(null, appContext);
+ .getMethod(UPDATE_METHOD, Context.class, Context.class)
+ .invoke(null, appContext, libContext);
return sMediaLibrary;
}
}
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
index 19f01c2..a1e2404 100644
--- a/media/java/android/media/update/StaticProvider.java
+++ b/media/java/android/media/update/StaticProvider.java
@@ -18,6 +18,7 @@
import android.annotation.SystemApi;
import android.widget.MediaController2;
+import android.widget.VideoView2;
/**
* Interface for connecting the public API to an updatable implementation.
@@ -31,4 +32,5 @@
public interface StaticProvider {
MediaController2Provider createMediaController2(
MediaController2 instance, ViewProvider superProvider);
+ VideoView2Provider createVideoView2(VideoView2 instance, ViewProvider superProvider);
}
diff --git a/media/java/android/media/update/VideoView2Provider.java b/media/java/android/media/update/VideoView2Provider.java
new file mode 100644
index 0000000..6fc9bdc
--- /dev/null
+++ b/media/java/android/media/update/VideoView2Provider.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.update;
+
+import android.media.AudioAttributes;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.widget.MediaController2;
+import android.widget.VideoView2;
+
+import java.util.Map;
+
+/**
+ * Interface for connecting the public API to an updatable implementation.
+ *
+ * Each instance object is connected to one corresponding updatable object which implements the
+ * runtime behavior of that class. There should a corresponding provider method for all public
+ * methods.
+ *
+ * All methods behave as per their namesake in the public API.
+ *
+ * @see android.widget.VideoView2
+ *
+ * @hide
+ */
+// TODO @SystemApi
+public interface VideoView2Provider extends ViewProvider {
+ void start_impl();
+ void pause_impl();
+ int getDuration_impl();
+ int getCurrentPosition_impl();
+ void seekTo_impl(int msec);
+ boolean isPlaying_impl();
+ int getBufferPercentage_impl();
+ int getAudioSessionId_impl();
+ void showSubtitle_impl();
+ void hideSubtitle_impl();
+ void setAudioFocusRequest_impl(int focusGain);
+ void setAudioAttributes_impl(AudioAttributes attributes);
+ void setVideoPath_impl(String path);
+ void setVideoURI_impl(Uri uri);
+ void setVideoURI_impl(Uri uri, Map<String, String> headers);
+ void setMediaController2_impl(MediaController2 controllerView);
+ void setViewType_impl(int viewType);
+ int getViewType_impl();
+ void stopPlayback_impl();
+ void setOnPreparedListener_impl(MediaPlayer.OnPreparedListener l);
+ void setOnCompletionListener_impl(MediaPlayer.OnCompletionListener l);
+ void setOnErrorListener_impl(MediaPlayer.OnErrorListener l);
+ void setOnInfoListener_impl(MediaPlayer.OnInfoListener l);
+ void setOnViewTypeChangedListener_impl(VideoView2.OnViewTypeChangedListener l);
+}
diff --git a/native/android/net.c b/native/android/net.c
index de4b90c..60296a7 100644
--- a/native/android/net.c
+++ b/native/android/net.c
@@ -27,7 +27,7 @@
static const uint32_t k32BitMask = 0xffffffff;
// This value MUST be kept in sync with the corresponding value in
// the android.net.Network#getNetworkHandle() implementation.
- static const uint32_t kHandleMagic = 0xfacade;
+ static const uint32_t kHandleMagic = 0xcafed00d;
// Check for minimum acceptable version of the API in the low bits.
if (handle != NETWORK_UNSPECIFIED &&
diff --git a/packages/BackupRestoreConfirmation/res/values-ar/strings.xml b/packages/BackupRestoreConfirmation/res/values-ar/strings.xml
index 4d4d6be..810b1e7 100644
--- a/packages/BackupRestoreConfirmation/res/values-ar/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-ar/strings.xml
@@ -21,7 +21,7 @@
<string name="backup_confirm_text" msgid="1878021282758896593">"تم طلب الاحتفاظ بنسخة احتياطية كاملة من البيانات على كمبيوتر سطح مكتب متصل. هل تريد السماح بإجراء ذلك؟\n\nإذا لم تطلب الاحتفاظ بنسخة احتياطية بنفسك، فلا تسمح بمتابعة العملية."</string>
<string name="allow_backup_button_label" msgid="4217228747769644068">"الاحتفاظ بنسخة احتياطية من بياناتي"</string>
<string name="deny_backup_button_label" msgid="6009119115581097708">"عدم النسخ الاحتياطي"</string>
- <string name="restore_confirm_text" msgid="7499866728030461776">"تم طلب استرداد جميع البيانات بالكامل من كمبيوتر سطح مكتب متصل. هل تريد السماح بإجراء ذلك؟\n\nإذا لم تطلب الاسترداد بنفسك، فلا تسمح بمتابعة العملية. يؤدي لك إلى استبدال أية بيانات حاليًا على الجهاز."</string>
+ <string name="restore_confirm_text" msgid="7499866728030461776">"تم طلب استرداد جميع البيانات بالكامل من كمبيوتر سطح مكتب متصل. هل تريد السماح بإجراء ذلك؟\n\nإذا لم تطلب الاسترداد بنفسك، فلا تسمح بمتابعة العملية. يؤدي لك إلى استبدال أي بيانات حاليًا على الجهاز."</string>
<string name="allow_restore_button_label" msgid="3081286752277127827">"استرداد بياناتي"</string>
<string name="deny_restore_button_label" msgid="1724367334453104378">"عدم الاسترداد"</string>
<string name="current_password_text" msgid="8268189555578298067">"يُرجى إدخال كلمة مرور النسخ الاحتياطي أدناه:"</string>
diff --git a/packages/BackupRestoreConfirmation/res/values-pt-rBR/strings.xml b/packages/BackupRestoreConfirmation/res/values-pt-rBR/strings.xml
index cbc579e..baa4867 100644
--- a/packages/BackupRestoreConfirmation/res/values-pt-rBR/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-pt-rBR/strings.xml
@@ -18,10 +18,10 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="backup_confirm_title" msgid="827563724209303345">"Backup completo"</string>
<string name="restore_confirm_title" msgid="5469365809567486602">"Restauração completa"</string>
- <string name="backup_confirm_text" msgid="1878021282758896593">"Foi solicitado um backup completo de todos os dados para um computador conectado. Deseja permitir que isso aconteça?\n\nCaso você não tenha solicitado o backup, não permita que a operação prossiga."</string>
+ <string name="backup_confirm_text" msgid="1878021282758896593">"Foi solicitado um backup completo de todos os dados para um computador conectado. Quer permitir que isso aconteça?\n\nCaso você não tenha solicitado o backup, não permita que a operação prossiga."</string>
<string name="allow_backup_button_label" msgid="4217228747769644068">"Fazer backup de meus dados"</string>
<string name="deny_backup_button_label" msgid="6009119115581097708">"Não fazer backup"</string>
- <string name="restore_confirm_text" msgid="7499866728030461776">"Foi solicitada uma restauração completa de todos os dados de um computador conectado. Deseja permitir que isso ocorra?\n\nCaso você não tenha solicitado a restauração, não permita que a operação prossiga. Isso substituirá todos os dados existentes no dispositivo!"</string>
+ <string name="restore_confirm_text" msgid="7499866728030461776">"Foi solicitada uma restauração completa de todos os dados de um computador conectado. Quer permitir que isso ocorra?\n\nCaso você não tenha solicitado a restauração, não permita que a operação prossiga. Isso substituirá todos os dados existentes no dispositivo!"</string>
<string name="allow_restore_button_label" msgid="3081286752277127827">"Restaurar meus dados"</string>
<string name="deny_restore_button_label" msgid="1724367334453104378">"Não restaurar"</string>
<string name="current_password_text" msgid="8268189555578298067">"Digite sua senha de backup atual abaixo:"</string>
diff --git a/packages/BackupRestoreConfirmation/res/values-pt/strings.xml b/packages/BackupRestoreConfirmation/res/values-pt/strings.xml
index cbc579e..baa4867 100644
--- a/packages/BackupRestoreConfirmation/res/values-pt/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-pt/strings.xml
@@ -18,10 +18,10 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="backup_confirm_title" msgid="827563724209303345">"Backup completo"</string>
<string name="restore_confirm_title" msgid="5469365809567486602">"Restauração completa"</string>
- <string name="backup_confirm_text" msgid="1878021282758896593">"Foi solicitado um backup completo de todos os dados para um computador conectado. Deseja permitir que isso aconteça?\n\nCaso você não tenha solicitado o backup, não permita que a operação prossiga."</string>
+ <string name="backup_confirm_text" msgid="1878021282758896593">"Foi solicitado um backup completo de todos os dados para um computador conectado. Quer permitir que isso aconteça?\n\nCaso você não tenha solicitado o backup, não permita que a operação prossiga."</string>
<string name="allow_backup_button_label" msgid="4217228747769644068">"Fazer backup de meus dados"</string>
<string name="deny_backup_button_label" msgid="6009119115581097708">"Não fazer backup"</string>
- <string name="restore_confirm_text" msgid="7499866728030461776">"Foi solicitada uma restauração completa de todos os dados de um computador conectado. Deseja permitir que isso ocorra?\n\nCaso você não tenha solicitado a restauração, não permita que a operação prossiga. Isso substituirá todos os dados existentes no dispositivo!"</string>
+ <string name="restore_confirm_text" msgid="7499866728030461776">"Foi solicitada uma restauração completa de todos os dados de um computador conectado. Quer permitir que isso ocorra?\n\nCaso você não tenha solicitado a restauração, não permita que a operação prossiga. Isso substituirá todos os dados existentes no dispositivo!"</string>
<string name="allow_restore_button_label" msgid="3081286752277127827">"Restaurar meus dados"</string>
<string name="deny_restore_button_label" msgid="1724367334453104378">"Não restaurar"</string>
<string name="current_password_text" msgid="8268189555578298067">"Digite sua senha de backup atual abaixo:"</string>
diff --git a/packages/BackupRestoreConfirmation/res/values-sw/strings.xml b/packages/BackupRestoreConfirmation/res/values-sw/strings.xml
index 530efc0..ed75344 100644
--- a/packages/BackupRestoreConfirmation/res/values-sw/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-sw/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="backup_confirm_title" msgid="827563724209303345">"Kuhifadhi kikamilifu"</string>
<string name="restore_confirm_title" msgid="5469365809567486602">"Kurejesha kila kitu"</string>
- <string name="backup_confirm_text" msgid="1878021282758896593">"Ombi la kuhifadhi nakala rudufu kamili za data kwenye eneo kazi la kompyuta iliyounganishwa limewasilishwa. Ungependa shughuli hii ufanyike?\n\n Ikiwa sio wewe uliyewasilisha ombi hili, usikubali shughuli hii iendelee."</string>
+ <string name="backup_confirm_text" msgid="1878021282758896593">"Ombi la kuhifadhi nakala kamili za data kwenye eneo kazi la kompyuta iliyounganishwa limewasilishwa. Ungependa shughuli hii ufanyike?\n\n Ikiwa si wewe uliyewasilisha ombi hili, usikubali shughuli hii iendelee."</string>
<string name="allow_backup_button_label" msgid="4217228747769644068">"Hifadhi nakala ya data yangu"</string>
<string name="deny_backup_button_label" msgid="6009119115581097708">"Usicheleze"</string>
<string name="restore_confirm_text" msgid="7499866728030461776">"Kurejesha kamili kwa data nzima kutoka kwa eneo kazi la kompyuta lililounganishwa limeombwa. Unataka kuruhusu hii kutendeka?\n\n Ikiwa hukuweza kurejesha upya mwenyewe, usiruhusu uendeshaji huu kuendelea. Hii itaweka upya data yoyote iliyo kwenye kifaa hiki sasa!"</string>
diff --git a/packages/CarrierDefaultApp/res/values-bs/strings.xml b/packages/CarrierDefaultApp/res/values-bs/strings.xml
index 7edbb11..ad2fc24 100644
--- a/packages/CarrierDefaultApp/res/values-bs/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-bs/strings.xml
@@ -4,12 +4,12 @@
<string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
<string name="android_system_label" msgid="2797790869522345065">"Mobilni operater"</string>
<string name="portal_notification_id" msgid="5155057562457079297">"Mobilni internet je potrošen"</string>
- <string name="no_data_notification_id" msgid="668400731803969521">"Prijenos mobilnih podataka je deaktiviran"</string>
+ <string name="no_data_notification_id" msgid="668400731803969521">"Prijenos podataka na mobilnoj mreži je deaktiviran"</string>
<string name="portal_notification_detail" msgid="2295729385924660881">"Dodirnite da posjetite %s web lokaciju"</string>
<string name="no_data_notification_detail" msgid="3112125343857014825">"Obratite se pružaocu usluga %s"</string>
<string name="no_mobile_data_connection_title" msgid="7449525772416200578">"Nema mobilnog prijenosa podataka"</string>
<string name="no_mobile_data_connection" msgid="544980465184147010">"Dodajte plan prijenosa podataka ili rominga putem operatera %s"</string>
- <string name="mobile_data_status_notification_channel_name" msgid="833999690121305708">"Status mobilnih podataka"</string>
+ <string name="mobile_data_status_notification_channel_name" msgid="833999690121305708">"Status prijenosa podataka na mobilnoj mreži"</string>
<string name="action_bar_label" msgid="4290345990334377177">"Prijava na mobilnu mrežu"</string>
<string name="ssl_error_warning" msgid="3127935140338254180">"Mreža kojoj pokušavate pristupiti ima sigurnosnih problema."</string>
<string name="ssl_error_example" msgid="6188711843183058764">"Naprimjer, stranica za prijavljivanje možda ne pripada prikazanoj organizaciji."</string>
diff --git a/packages/InputDevices/res/values-en-rAU/strings.xml b/packages/InputDevices/res/values-en-rAU/strings.xml
index 9217bc4..8f37882 100644
--- a/packages/InputDevices/res/values-en-rAU/strings.xml
+++ b/packages/InputDevices/res/values-en-rAU/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanish (Latin)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvian"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persian"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerbaijani"</string>
</resources>
diff --git a/packages/InputDevices/res/values-en-rCA/strings.xml b/packages/InputDevices/res/values-en-rCA/strings.xml
index 9217bc4..8f37882 100644
--- a/packages/InputDevices/res/values-en-rCA/strings.xml
+++ b/packages/InputDevices/res/values-en-rCA/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanish (Latin)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvian"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persian"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerbaijani"</string>
</resources>
diff --git a/packages/InputDevices/res/values-en-rGB/strings.xml b/packages/InputDevices/res/values-en-rGB/strings.xml
index 9217bc4..8f37882 100644
--- a/packages/InputDevices/res/values-en-rGB/strings.xml
+++ b/packages/InputDevices/res/values-en-rGB/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanish (Latin)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvian"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persian"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerbaijani"</string>
</resources>
diff --git a/packages/InputDevices/res/values-en-rIN/strings.xml b/packages/InputDevices/res/values-en-rIN/strings.xml
index 9217bc4..8f37882 100644
--- a/packages/InputDevices/res/values-en-rIN/strings.xml
+++ b/packages/InputDevices/res/values-en-rIN/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanish (Latin)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvian"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persian"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerbaijani"</string>
</resources>
diff --git a/packages/InputDevices/res/values-en-rXC/strings.xml b/packages/InputDevices/res/values-en-rXC/strings.xml
index 43d76770..bc1b234 100644
--- a/packages/InputDevices/res/values-en-rXC/strings.xml
+++ b/packages/InputDevices/res/values-en-rXC/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanish (Latin)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvian"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persian"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerbaijani"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ar/strings.xml b/packages/PrintSpooler/res/values-ar/strings.xml
index eab784d..f1fd93b 100644
--- a/packages/PrintSpooler/res/values-ar/strings.xml
+++ b/packages/PrintSpooler/res/values-ar/strings.xml
@@ -56,7 +56,7 @@
<string name="print_select_printer" msgid="7388760939873368698">"حدد الطابعة"</string>
<string name="print_forget_printer" msgid="5035287497291910766">"تجاهل الطابعة"</string>
<plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
- <item quantity="zero">لم يتم العثور على أية طابعة (<xliff:g id="COUNT_1">%1$s</xliff:g>)</item>
+ <item quantity="zero">لم يتم العثور على أي طابعة (<xliff:g id="COUNT_1">%1$s</xliff:g>)</item>
<item quantity="two">تم العثور على طابعتين (<xliff:g id="COUNT_1">%1$s</xliff:g>)</item>
<item quantity="few">تم العثور على <xliff:g id="COUNT_1">%1$s</xliff:g> طابعات</item>
<item quantity="many">تم العثور على <xliff:g id="COUNT_1">%1$s</xliff:g> طابعة</item>
diff --git a/packages/PrintSpooler/res/values-mr/strings.xml b/packages/PrintSpooler/res/values-mr/strings.xml
index 862d193..7fe9c8c 100644
--- a/packages/PrintSpooler/res/values-mr/strings.xml
+++ b/packages/PrintSpooler/res/values-mr/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="4469836075319831821">"स्पूलर मुद्रण"</string>
+ <string name="app_label" msgid="4469836075319831821">"स्पूलर प्रिंट"</string>
<string name="more_options_button" msgid="2243228396432556771">"अधिक पर्याय"</string>
<string name="label_destination" msgid="9132510997381599275">"गंतव्यस्थान"</string>
<string name="label_copies" msgid="3634531042822968308">"प्रती"</string>
@@ -31,22 +31,22 @@
<string name="template_all_pages" msgid="3322235982020148762">"सर्व <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> ची श्रेणी"</string>
<string name="pages_range_example" msgid="8558694453556945172">"उदा. 1—5,8,11—13"</string>
- <string name="print_preview" msgid="8010217796057763343">"मुद्रण पूर्वावलोकन"</string>
+ <string name="print_preview" msgid="8010217796057763343">"प्रिंट पूर्वावलोकन"</string>
<string name="install_for_print_preview" msgid="6366303997385509332">"पूर्वावलोकनासाठी पीडीएफ व्ह्यूअर इंस्टॉल करा"</string>
<string name="printing_app_crashed" msgid="854477616686566398">"प्रिंटिंग अॅप क्रॅश झाले"</string>
- <string name="generating_print_job" msgid="3119608742651698916">"मुद्रण कार्य व्युत्पन्न करत आहे"</string>
+ <string name="generating_print_job" msgid="3119608742651698916">"प्रिंट कार्य व्युत्पन्न करत आहे"</string>
<string name="save_as_pdf" msgid="5718454119847596853">"पीडीएफ म्हणून सेव्ह करा"</string>
<string name="all_printers" msgid="5018829726861876202">"सर्व प्रिंटर..."</string>
- <string name="print_dialog" msgid="32628687461331979">"मुद्रण संवाद"</string>
+ <string name="print_dialog" msgid="32628687461331979">"प्रिंट संवाद"</string>
<string name="current_page_template" msgid="5145005201131935302">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g>/<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
<string name="page_description_template" msgid="6831239682256197161">"<xliff:g id="PAGE_COUNT">%2$d</xliff:g> पैकी <xliff:g id="CURRENT_PAGE">%1$d</xliff:g> पृष्ठ"</string>
<string name="summary_template" msgid="8899734908625669193">"सारांश, प्रती <xliff:g id="COPIES">%1$s</xliff:g>, कागद आकार <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
<string name="expand_handle" msgid="7282974448109280522">"विस्तृत करण्याचे हँडल"</string>
<string name="collapse_handle" msgid="6886637989442507451">"संक्षिप्त करण्याचे हँडल"</string>
- <string name="print_button" msgid="645164566271246268">"मुद्रण करा"</string>
+ <string name="print_button" msgid="645164566271246268">"प्रिंट करा"</string>
<string name="savetopdf_button" msgid="2976186791686924743">"पीडीएफ वर सेव्ह करा"</string>
- <string name="print_options_expanded" msgid="6944679157471691859">"मुद्रण पर्याय विस्तृत झाले"</string>
- <string name="print_options_collapsed" msgid="7455930445670414332">"मुद्रण पर्याय संक्षिप्त झाले"</string>
+ <string name="print_options_expanded" msgid="6944679157471691859">"प्रिंट पर्याय विस्तृत झाले"</string>
+ <string name="print_options_collapsed" msgid="7455930445670414332">"प्रिंट पर्याय संक्षिप्त झाले"</string>
<string name="search" msgid="5421724265322228497">"शोध"</string>
<string name="all_printers_label" msgid="3178848870161526399">"सर्व प्रिंटर"</string>
<string name="add_print_service_label" msgid="5356702546188981940">"सेवा जोडा"</string>
@@ -64,9 +64,9 @@
<string name="notification_channel_progress" msgid="872788690775721436">"प्रिंट कार्ये चालवणे"</string>
<string name="notification_channel_failure" msgid="9042250774797916414">"अयशस्वी प्रिंट कार्ये"</string>
<string name="could_not_create_file" msgid="3425025039427448443">"फाईल तयार करणेे शक्य झाले नाही"</string>
- <string name="print_services_disabled_toast" msgid="9089060734685174685">"काही मुद्रण सेवा अक्षम केल्या आहेत"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"काही प्रिंट सेवा अक्षम केल्या आहेत"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"प्रिंटर शोधत आहे"</string>
- <string name="print_no_print_services" msgid="8561247706423327966">"कोणत्याही मुद्रण सेवा सक्षम केलेल्या नाहीत"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"कोणत्याही प्रिंट सेवा सक्षम केलेल्या नाहीत"</string>
<string name="print_no_printers" msgid="4869403323900054866">"कोणतेही प्रिंटर आढळले नाही"</string>
<string name="cannot_add_printer" msgid="7840348733668023106">"प्रिंटर जोडू शकत नाही"</string>
<string name="select_to_add_printers" msgid="3800709038689830974">"प्रिंटर जोडण्यासाठी निवडा"</string>
@@ -79,7 +79,7 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$s</xliff:g> प्रिंटर शोधण्यासाठी इंस्टॉल करा</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> प्रिंटर शोधण्यासाठी इंस्टॉल करा</item>
</plurals>
- <string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> मुद्रण करत आहे"</string>
+ <string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> प्रिंट करत आहे"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> रद्द करत आहे"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"प्रिंटर एरर <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="blocked_notification_title_template" msgid="1175435827331588646">"प्रिंटरने <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> अवरोधित केले"</string>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
index e0a3f6c..6c74418 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
@@ -59,7 +59,9 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.HandlerCaller;
+import com.android.internal.print.DualDumpOutputStream;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.printspooler.R;
import com.android.printspooler.util.ApprovedPrintServices;
@@ -159,44 +161,11 @@
return new PrintSpooler();
}
- private void dumpLocked(PrintWriter pw, String[] args) {
- String prefix = (args.length > 0) ? args[0] : "";
- String tab = " ";
-
- pw.append(prefix).append("print jobs:").println();
- final int printJobCount = mPrintJobs.size();
- for (int i = 0; i < printJobCount; i++) {
- PrintJobInfo printJob = mPrintJobs.get(i);
- pw.append(prefix).append(tab).append(printJob.toString());
- pw.println();
- }
-
- pw.append(prefix).append("print job files:").println();
- File[] files = getFilesDir().listFiles();
- if (files != null) {
- final int fileCount = files.length;
- for (int i = 0; i < fileCount; i++) {
- File file = files[i];
- if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
- pw.append(prefix).append(tab).append(file.getName()).println();
- }
- }
- }
-
- pw.append(prefix).append("approved print services:").println();
- Set<String> approvedPrintServices = (new ApprovedPrintServices(this)).getApprovedServices();
- if (approvedPrintServices != null) {
- for (String approvedService : approvedPrintServices) {
- pw.append(prefix).append(tab).append(approvedService).println();
- }
- }
- }
-
- private void dumpLocked(@NonNull ProtoOutputStream proto) {
+ private void dumpLocked(@NonNull DualDumpOutputStream dumpStream) {
int numPrintJobs = mPrintJobs.size();
for (int i = 0; i < numPrintJobs; i++) {
- writePrintJobInfo(this, proto, PrintSpoolerInternalStateProto.PRINT_JOBS,
- mPrintJobs.get(i));
+ writePrintJobInfo(this, dumpStream, "print_jobs",
+ PrintSpoolerInternalStateProto.PRINT_JOBS, mPrintJobs.get(i));
}
File[] files = getFilesDir().listFiles();
@@ -204,7 +173,8 @@
for (int i = 0; i < files.length; i++) {
File file = files[i];
if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
- proto.write(PrintSpoolerInternalStateProto.PRINT_JOB_FILES, file.getName());
+ dumpStream.write("print_job_files",
+ PrintSpoolerInternalStateProto.PRINT_JOB_FILES, file.getName());
}
}
}
@@ -214,13 +184,13 @@
for (String approvedService : approvedPrintServices) {
ComponentName componentName = ComponentName.unflattenFromString(approvedService);
if (componentName != null) {
- writeComponentName(proto, PrintSpoolerInternalStateProto.APPROVED_SERVICES,
- componentName);
+ writeComponentName(dumpStream, "approved_services",
+ PrintSpoolerInternalStateProto.APPROVED_SERVICES, componentName);
}
}
}
- proto.flush();
+ dumpStream.flush();
}
@Override
@@ -244,9 +214,15 @@
try {
synchronized (mLock) {
if (dumpAsProto) {
- dumpLocked(new ProtoOutputStream(fd));
+ dumpLocked(new DualDumpOutputStream(new ProtoOutputStream(fd), null));
} else {
- dumpLocked(pw, args);
+ try (FileOutputStream out = new FileOutputStream(fd)) {
+ try (PrintWriter w = new PrintWriter(out)) {
+ dumpLocked(new DualDumpOutputStream(null, new IndentingPrintWriter(w,
+ " ")));
+ }
+ } catch (IOException ignored) {
+ }
}
}
} finally {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
index 8b00ed0..faa10cc 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
@@ -23,6 +23,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
+
import com.android.printspooler.R;
/**
@@ -410,7 +411,7 @@
mPrintButton.offsetTopAndBottom(dy);
- mDraggableContent.notifySubtreeAccessibilityStateChangedIfNeeded();
+ mDraggableContent.notifyAccessibilitySubtreeChanged();
onDragProgress(progress);
}
diff --git a/packages/SettingsLib/res/layout/settings_with_drawer.xml b/packages/SettingsLib/res/layout/settings_with_drawer.xml
index 55c192d..e1d5c0f 100644
--- a/packages/SettingsLib/res/layout/settings_with_drawer.xml
+++ b/packages/SettingsLib/res/layout/settings_with_drawer.xml
@@ -13,34 +13,28 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<android.support.v4.widget.DrawerLayout
+<!-- The main content view -->
+<LinearLayout
+ android:id="@+id/content_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/drawer_layout"
android:layout_width="match_parent"
- android:layout_height="match_parent">
- <!-- The main content view -->
- <LinearLayout
- android:id="@+id/content_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <Toolbar
+ android:id="@+id/action_bar"
+ style="?android:attr/actionBarStyle"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:fitsSystemWindows="true">
- <Toolbar
- android:id="@+id/action_bar"
- style="?android:attr/actionBarStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:theme="?android:attr/actionBarTheme"
- android:navigationContentDescription="@*android:string/action_bar_up_description"/>
- <FrameLayout
- android:id="@+id/content_header_container"
- style="?android:attr/actionBarStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- <FrameLayout
- android:id="@+id/content_frame"
- android:layout_width="match_parent"
- android:layout_height="fill_parent"
- android:background="?android:attr/windowBackground" />
- </LinearLayout>
-</android.support.v4.widget.DrawerLayout>
+ android:layout_height="wrap_content"
+ android:theme="?android:attr/actionBarTheme"
+ android:navigationContentDescription="@*android:string/action_bar_up_description" />
+ <FrameLayout
+ android:id="@+id/content_header_container"
+ style="?android:attr/actionBarStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ <FrameLayout
+ android:id="@+id/content_frame"
+ android:layout_width="match_parent"
+ android:layout_height="fill_parent"
+ android:background="?android:attr/windowBackground" />
+</LinearLayout>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index b035b70..d130a61 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -498,8 +498,6 @@
<string name="wifi_display_certification">Wireless display certification</string>
<!-- Setting Checkbox title whether to enable WiFi Verbose Logging. [CHAR LIMIT=40] -->
<string name="wifi_verbose_logging">Enable Wi\u2011Fi Verbose Logging</string>
- <!-- Setting Checkbox title whether to enable WiFi Aggressive Handover (more actively switch from wifi to mobile data). [CHAR LIMIT=40] -->
- <string name="wifi_aggressive_handover">Aggressive Wi\u2011Fi to mobile handover</string>
<!-- Setting Checkbox title whether to enable WiFi Scanning in the presence of traffic. [CHAR LIMIT=80] -->
<string name="wifi_allow_scan_with_traffic">Always allow Wi\u2011Fi Roam Scans</string>
<!-- Setting Checkbox title whether to always keep mobile data active. [CHAR LIMIT=80] -->
@@ -556,8 +554,6 @@
<string name="wifi_display_certification_summary">Show options for wireless display certification</string>
<!-- Setting Checkbox summary whether to enable Wifi verbose Logging [CHAR LIMIT=80] -->
<string name="wifi_verbose_logging_summary">Increase Wi\u2011Fi logging level, show per SSID RSSI in Wi\u2011Fi Picker</string>
- <!-- Setting Checkbox summary whether to enable Wifi aggressive handover [CHAR LIMIT=130] -->
- <string name="wifi_aggressive_handover_summary">When enabled, Wi\u2011Fi will be more aggressive in handing over the data connection to mobile, when Wi\u2011Fi signal is low</string>
<!-- Setting Checkbox summary whether to always allow WiFi Roam Scans [CHAR LIMIT=130] -->
<string name="wifi_allow_scan_with_traffic_summary">Allow/Disallow Wi\u2011Fi Roam Scans based on the amount of data traffic present at the interface</string>
<!-- UI debug setting: limit size of Android logger buffers -->
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
index adb4832..ae24c07 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
@@ -16,9 +16,9 @@
package com.android.settingslib.core.lifecycle;
import static android.arch.lifecycle.Lifecycle.Event.ON_START;
-
import static com.google.common.truth.Truth.assertThat;
+import android.arch.lifecycle.LifecycleOwner;
import android.content.Context;
import android.view.Menu;
import android.view.MenuInflater;
@@ -48,6 +48,7 @@
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class LifecycleTest {
+ private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
public static class TestDialogFragment extends ObservableDialogFragment {
@@ -146,7 +147,8 @@
@Before
public void setUp() {
- mLifecycle = new Lifecycle(() -> mLifecycle);
+ mLifecycleOwner = () -> mLifecycle;
+ mLifecycle = new Lifecycle(mLifecycleOwner);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
index 5d5733e4..050877d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
@@ -17,11 +17,11 @@
package com.android.settingslib.development;
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
+import android.arch.lifecycle.LifecycleOwner;
import android.os.SystemProperties;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
@@ -44,6 +44,7 @@
shadows = SystemPropertiesTestImpl.class)
public class LogpersistPreferenceControllerTest {
+ private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
@Mock
@@ -57,7 +58,8 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
SystemProperties.set("ro.debuggable", "1");
- mLifecycle = new Lifecycle(() -> mLifecycle);
+ mLifecycleOwner = () -> mLifecycle;
+ mLifecycle = new Lifecycle(mLifecycleOwner);
mController = new AbstractLogpersistPreferenceController(RuntimeEnvironment.application,
mLifecycle) {
@Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
index 75b6c5f..88c57b5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
@@ -17,13 +17,13 @@
package com.android.settingslib.widget;
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.arch.lifecycle.LifecycleOwner;
import android.support.v14.preference.PreferenceFragment;
import android.support.v7.preference.PreferenceManager;
import android.support.v7.preference.PreferenceScreen;
@@ -49,13 +49,15 @@
@Mock
private PreferenceScreen mScreen;
+ private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
private FooterPreferenceMixin mMixin;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mLifecycle = new Lifecycle(() -> mLifecycle);
+ mLifecycleOwner = () -> mLifecycle;
+ mLifecycle = new Lifecycle(mLifecycleOwner);
when(mFragment.getPreferenceManager()).thenReturn(mock(PreferenceManager.class));
when(mFragment.getPreferenceManager().getContext())
.thenReturn(ShadowApplication.getInstance().getApplicationContext());
diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml
index 287a888..fd4d296 100644
--- a/packages/SettingsProvider/AndroidManifest.xml
+++ b/packages/SettingsProvider/AndroidManifest.xml
@@ -8,6 +8,7 @@
android:process="system"
android:backupAgent="SettingsBackupAgent"
android:killAfterRestore="false"
+ android:restoreAnyVersion="true"
android:icon="@mipmap/ic_launcher_settings"
android:defaultToDeviceProtectedStorage="true"
android:directBootAware="true">
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 1be0645..48a3a30 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -28,7 +28,7 @@
<string name="def_bluetooth_disabled_profiles" translatable="false">0</string>
<bool name="def_auto_time">true</bool>
<bool name="def_auto_time_zone">true</bool>
- <bool name="def_accelerometer_rotation">true</bool>
+ <bool name="def_accelerometer_rotation">false</bool>
<!-- Default screen brightness, from 0 to 255. 102 is 40%. -->
<integer name="def_screen_brightness">102</integer>
<bool name="def_screen_brightness_automatic_mode">false</bool>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index ae88227..c7ba4d6 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -31,10 +31,12 @@
import android.net.Uri;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
+import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.BackupUtils;
import android.util.Log;
@@ -50,6 +52,7 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -147,6 +150,13 @@
// stored in the full-backup tarfile as well, so should not be changed.
private static final String STAGE_FILE = "flattened-data";
+ // List of keys that support restore to lower version of the SDK, introduced in Android P
+ private static final ArraySet<String> RESTORE_FROM_HIGHER_SDK_INT_SUPPORTED_KEYS =
+ new ArraySet<String>(Arrays.asList(new String[] {
+ KEY_NETWORK_POLICIES,
+ KEY_WIFI_NEW_CONFIG,
+ }));
+
private SettingsHelper mSettingsHelper;
private WifiManager mWifiManager;
@@ -209,6 +219,10 @@
public void onRestore(BackupDataInput data, int appVersionCode,
ParcelFileDescriptor newState) throws IOException {
+ if (DEBUG) {
+ Log.d(TAG, "onRestore(): appVersionCode: " + appVersionCode
+ + "; Build.VERSION.SDK_INT: " + Build.VERSION.SDK_INT);
+ }
// versionCode of com.android.providers.settings corresponds to SDK_INT
mRestoredFromSdkInt = appVersionCode;
@@ -221,6 +235,15 @@
while (data.readNextHeader()) {
final String key = data.getKey();
final int size = data.getDataSize();
+
+ // bail out of restoring from higher SDK_INT version for unsupported keys
+ if (appVersionCode > Build.VERSION.SDK_INT
+ && !RESTORE_FROM_HIGHER_SDK_INT_SUPPORTED_KEYS.contains(key)) {
+ Log.w(TAG, "Not restoring unrecognized key '"
+ + key + "' from future version " + appVersionCode);
+ continue;
+ }
+
switch (key) {
case KEY_SYSTEM :
restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 48de1c9..d33e084 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1119,6 +1119,12 @@
dumpSetting(s, p,
Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
GlobalSettingsProto.NOTIFICATION_SNOOZE_OPTIONS);
+ dumpSetting(s, p,
+ Settings.Global.ZRAM_ENABLED,
+ GlobalSettingsProto.ZRAM_ENABLED);
+ dumpSetting(s, p,
+ Settings.Global.ENABLE_SMART_REPLIES_IN_NOTIFICATIONS,
+ GlobalSettingsProto.ENABLE_SMART_REPLIES_IN_NOTIFICATIONS);
}
/** Dump a single {@link SettingsState.Setting} to a proto buf */
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1167d69..175cff6 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1584,6 +1584,11 @@
restriction = UserManager.DISALLOW_SAFE_BOOT;
break;
+ case Settings.Global.AIRPLANE_MODE_ON:
+ if ("0".equals(value)) return false;
+ restriction = UserManager.DISALLOW_AIRPLANE_MODE;
+ break;
+
default:
if (setting != null && setting.startsWith(Settings.Global.DATA_ROAMING)) {
if ("0".equals(value)) return false;
@@ -2940,7 +2945,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 150;
+ private static final int SETTINGS_VERSION = 151;
private final int mUserId;
@@ -3533,6 +3538,18 @@
currentVersion = 150;
}
+ if (currentVersion == 150) {
+ // Version 151: Reset rotate locked setting for upgrading users
+ final SettingsState systemSettings = getSystemSettingsLocked(userId);
+ systemSettings.insertSettingLocked(
+ Settings.System.ACCELEROMETER_ROTATION,
+ getContext().getResources().getBoolean(
+ R.bool.def_accelerometer_rotation) ? "1" : "0",
+ null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+
+ currentVersion = 151;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index b3d6357..0f43db0 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -134,6 +134,7 @@
<!-- Permission needed to access privileged VR APIs -->
<uses-permission android:name="android.permission.RESTRICTED_VR_ACCESS" />
<uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE" />
+ <uses-permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS" />
<application android:label="@string/app_label"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/Shell/res/values-eu/strings.xml b/packages/Shell/res/values-eu/strings.xml
index 78295e6..9695e41 100644
--- a/packages/Shell/res/values-eu/strings.xml
+++ b/packages/Shell/res/values-eu/strings.xml
@@ -28,7 +28,7 @@
<string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Hautatu hau akatsen txostena argazkirik gabe partekatzeko edo itxaron pantaila-argazkia atera arte"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Sakatu akatsen txostena argazkirik gabe partekatzeko edo itxaron pantaila-argazkia atera arte"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Sakatu akatsen txostena argazkirik gabe partekatzeko edo itxaron pantaila-argazkia atera arte"</string>
- <string name="bugreport_confirm" msgid="5917407234515812495">"Errore-txostenek sistemaren erregistro-fitxategietako datuak dauzkate eta, haietan, isilpekotzat jotzen duzun informazioa ager daiteke (adibidez, aplikazioen erabilera eta kokapen-datuak). Errore-txostenak partekatzen badituzu, partekatu soilik pertsona eta aplikazio fidagarriekin."</string>
+ <string name="bugreport_confirm" msgid="5917407234515812495">"Errore-txostenek sistemaren erregistro-fitxategietako datuak dauzkate eta, haietan, kontuzkotzat jotzen duzun informazioa ager daiteke (adibidez, aplikazioen erabilera eta kokapen-datuak). Errore-txostenak partekatzen badituzu, partekatu soilik pertsona eta aplikazio fidagarriekin."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Ez erakutsi berriro"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Akatsen txostenak"</string>
<string name="bugreport_unreadable_text" msgid="586517851044535486">"Ezin izan da irakurri akatsen txostena"</string>
diff --git a/packages/Shell/res/values-ne/strings.xml b/packages/Shell/res/values-ne/strings.xml
index 8079210..eadfeb9 100644
--- a/packages/Shell/res/values-ne/strings.xml
+++ b/packages/Shell/res/values-ne/strings.xml
@@ -23,9 +23,9 @@
<string name="bugreport_updating_title" msgid="4423539949559634214">"बग रिपोर्टमा विवरणहरू थप्दै"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"कृपया प्रतीक्षा गर्नुहोला..."</string>
<string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"उक्त बग सम्बन्धी रिपोर्ट चाँडै नै यस फोनमा देखा पर्नेछ"</string>
- <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"तपाईंको बग रिपोर्ट आदान-प्रदान गर्न चयन गर्नुहोस्"</string>
+ <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"तपाईंको बग रिपोर्ट आदान प्रदान गर्न चयन गर्नुहोस्"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"तपाईंको बग रिपोर्टलाई साझेदारी गर्न ट्याप गर्नुहोस्"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"तपाईंको बग रिपोर्ट स्क्रिनसट बिना आदान-प्रदान गर्नका लागि चयन गर्नुहोस् वा स्क्रिनसट लिने प्रक्रिया पूरा हुने प्रतीक्षा गर्नुहोस्"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"तपाईंको बग रिपोर्ट स्क्रिनसट बिना आदान प्रदान गर्नका लागि चयन गर्नुहोस् वा स्क्रिनसट लिने प्रक्रिया पूरा हुने प्रतीक्षा गर्नुहोस्"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"तपाईँको बग रिपोर्टलाई स्क्रिनसट बिना साझेदारी गर्नका लागि ट्याप गर्नुहोस् वा स्क्रिनसट लिने प्रक्रिया पूरा हुन प्रतीक्षा गर्नुहोस्"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"तपाईँको बग रिपोर्टलाई स्क्रिनसट बिना साझेदारी गर्नका लागि ट्याप गर्नुहोस् वा स्क्रिनसट लिने प्रक्रिया पूरा हुन प्रतीक्षा गर्नुहोस्"</string>
<string name="bugreport_confirm" msgid="5917407234515812495">"बग रिपोर्टहरूमा प्रणालीका विभिन्न लग फाइलहरूको डेटा हुन्छ जसमा तपाईँले संवेदनशील मानेको डेटा समावेश हुन सक्छ (जस्तै अनुप्रयोगको प्रयोग र स्थान सम्बन्धी डेटा)। तपाईँले विश्वास गर्ने व्यक्ति र अनुप्रयोगहरूसँग मात्र बग रिपोर्टहरूलाई साझेदारी गर्नुहोस्।"</string>
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index a8b184c..600f0dc 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -616,7 +616,7 @@
final IWindowManager wm = IWindowManager.Stub
.asInterface(ServiceManager.getService(Context.WINDOW_SERVICE));
try {
- wm.dismissKeyguard(null);
+ wm.dismissKeyguard(null, null);
} catch (Exception e) {
// ignore it
}
@@ -1909,7 +1909,7 @@
}
final IDumpstate dumpstate = IDumpstate.Stub.asInterface(service);
try {
- token = dumpstate.setListener("Shell", this);
+ token = dumpstate.setListener("Shell", this, /* perSectionDetails= */ false);
if (token != null) {
token.asBinder().linkToDeath(this, 0);
}
@@ -1978,6 +1978,15 @@
info.realMax = maxProgress;
}
+ @Override
+ public void onSectionComplete(String title, int status, int size, int durationMs)
+ throws RemoteException {
+ if (DEBUG) {
+ Log.v(TAG, "Title: " + title + " Status: " + status + " Size: " + size
+ + " Duration: " + durationMs + "ms");
+ }
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("token: "); pw.println(token);
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index c52c0aa..61f7fe8 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -108,6 +108,7 @@
public Supplier<Icon> iconSupplier;
public int state = Tile.STATE_ACTIVE;
public CharSequence label;
+ public CharSequence secondaryLabel;
public CharSequence contentDescription;
public CharSequence dualLabelContentDescription;
public boolean disabledByPolicy;
@@ -122,6 +123,7 @@
final boolean changed = !Objects.equals(other.icon, icon)
|| !Objects.equals(other.iconSupplier, iconSupplier)
|| !Objects.equals(other.label, label)
+ || !Objects.equals(other.secondaryLabel, secondaryLabel)
|| !Objects.equals(other.contentDescription, contentDescription)
|| !Objects.equals(other.dualLabelContentDescription,
dualLabelContentDescription)
@@ -135,6 +137,7 @@
other.icon = icon;
other.iconSupplier = iconSupplier;
other.label = label;
+ other.secondaryLabel = secondaryLabel;
other.contentDescription = contentDescription;
other.dualLabelContentDescription = dualLabelContentDescription;
other.expandedAccessibilityClassName = expandedAccessibilityClassName;
@@ -156,6 +159,7 @@
sb.append(",icon=").append(icon);
sb.append(",iconSupplier=").append(iconSupplier);
sb.append(",label=").append(label);
+ sb.append(",secondaryLabel=").append(secondaryLabel);
sb.append(",contentDescription=").append(contentDescription);
sb.append(",dualLabelContentDescription=").append(dualLabelContentDescription);
sb.append(",expandedAccessibilityClassName=").append(expandedAccessibilityClassName);
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java
index 56a3ee3..e25930c 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java
@@ -50,5 +50,7 @@
}
void setDarkIntensity(float intensity);
+
+ void setDelayTouchFeedback(boolean shouldDelay);
}
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
index 674ed5a..6131acc 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
@@ -14,6 +14,7 @@
package com.android.systemui.plugins.statusbar.phone;
+import android.graphics.Canvas;
import android.view.MotionEvent;
import com.android.systemui.plugins.Plugin;
@@ -35,6 +36,12 @@
public void setBarState(boolean vertical, boolean isRtl);
+ public void onDraw(Canvas canvas);
+
+ public void onDarkIntensityChange(float intensity);
+
+ public void onLayout(boolean changed, int left, int top, int right, int bottom);
+
public default void destroy() { }
}
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index c97cfc4..9adb550 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -34,7 +34,6 @@
android:orientation="vertical">
<RelativeLayout
android:id="@+id/keyguard_clock_container"
- android:animateLayoutChanges="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|top">
@@ -51,16 +50,6 @@
android:format12Hour="@string/keyguard_widget_12_hours_format"
android:format24Hour="@string/keyguard_widget_24_hours_format"
android:layout_marginBottom="@dimen/bottom_text_spacing_digital" />
- <com.android.systemui.ChargingView
- android:id="@+id/battery_doze"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignTop="@id/clock_view"
- android:layout_alignBottom="@id/clock_view"
- android:layout_toEndOf="@id/clock_view"
- android:visibility="invisible"
- android:src="@drawable/ic_aod_charging_24dp"
- android:contentDescription="@string/accessibility_ambient_display_charging" />
<View
android:id="@+id/clock_separator"
android:layout_width="16dp"
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index 7027d6f..b3ed8f5 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -101,7 +101,7 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="8364140853305528449">"لقد رسمت نقش إلغاء التأمين بشكل غير صحيح <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد إجراء <xliff:g id="NUMBER_1">%2$d</xliff:g> من المحاولات غير الناجحة الأخرى، ستُطالب بإلغاء تأمين الهاتف باستخدام حساب بريد إلكتروني لإلغاء تأمين الهاتف.\n\n أعد المحاولة خلال <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانية."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="3389829202093674267">"رمز \"رقم التعريف الشخصي\" لشريحة SIM غير صحيح، ويلزمك الاتصال الآن بمشغّل شبكة الجوّال لإلغاء قفل الجهاز."</string>
<plurals name="kg_password_wrong_pin_code" formatted="false" msgid="4314341367727055967">
- <item quantity="zero">رمز رقم التعريف الشخصي لشريحة SIM غير صحيح، ولم تتبق لديك أية محاولات (<xliff:g id="NUMBER_1">%d</xliff:g>).</item>
+ <item quantity="zero">رمز رقم التعريف الشخصي لشريحة SIM غير صحيح، ولم تتبق لديك أي محاولات (<xliff:g id="NUMBER_1">%d</xliff:g>).</item>
<item quantity="two">رمز رقم التعريف الشخصي لشريحة SIM غير صحيح، ويتبقى لديك محاولتان (<xliff:g id="NUMBER_1">%d</xliff:g>).</item>
<item quantity="few">رمز رقم التعريف الشخصي لشريحة SIM غير صحيح، ويتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولات.</item>
<item quantity="many">رمز رقم التعريف الشخصي لشريحة SIM غير صحيح، ويتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولة.</item>
@@ -110,7 +110,7 @@
</plurals>
<string name="kg_password_wrong_puk_code_dead" msgid="3329017604125179374">"شريحة SIM غير صالحة للاستخدام. يُرجى الاتصال بمشغّل شبكة الجوّال."</string>
<plurals name="kg_password_wrong_puk_code" formatted="false" msgid="2287504898931957513">
- <item quantity="zero">رمز PUK لشريحة SIM غير صحيح، ولم تتبق لديك أية محاولات (<xliff:g id="NUMBER_1">%d</xliff:g>) تصبح بعدها شريحة SIM غير صالحة للاستخدام بشكل دائم.</item>
+ <item quantity="zero">رمز PUK لشريحة SIM غير صحيح، ولم تتبق لديك أي محاولات (<xliff:g id="NUMBER_1">%d</xliff:g>) تصبح بعدها شريحة SIM غير صالحة للاستخدام بشكل دائم.</item>
<item quantity="two">رمز PUK لشريحة SIM غير صحيح، ويتبقى لديك محاولتان (<xliff:g id="NUMBER_1">%d</xliff:g>) تصبح بعدها شريحة SIM غير صالحة للاستخدام بشكل دائم.</item>
<item quantity="few">رمز PUK لشريحة SIM غير صحيح، ويتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولات تصبح بعدها شريحة SIM غير صالحة للاستخدام بشكل دائم.</item>
<item quantity="many">رمز PUK لشريحة SIM غير صحيح، ويتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولة تصبح بعدها شريحة SIM غير صالحة للاستخدام بشكل دائم.</item>
diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml
index b74a618..1f6addf 100644
--- a/packages/SystemUI/res-keyguard/values-bs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml
@@ -147,8 +147,8 @@
<item quantity="other">Unesite PIN kôd za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
</plurals>
<plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
- <item quantity="one">SIM kartica je onemogućena. Unesite PUK kôd da nastavite. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaj prije nego što SIM kartica postane trajno neupotrebljiva. Obratite se operateru za više informacija.</item>
- <item quantity="few">SIM kartica je onemogućena. Unesite PUK kôd da nastavite. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva. Obratite se operateru za više informacija.</item>
- <item quantity="other">SIM kartica je onemogućena. Unesite PUK kôd da nastavite. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva. Obratite se operateru za više informacija.</item>
+ <item quantity="one">SIM kartica je onemogućena. Unesite PUK kôd da nastavite. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaj prije nego što SIM kartica postane trajno neupotrebljiva. Za više informacija kontaktirajte mobilnog operatera.</item>
+ <item quantity="few">SIM kartica je onemogućena. Unesite PUK kôd da nastavite. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva. Za više informacija kontaktirajte mobilnog operatera.</item>
+ <item quantity="other">SIM kartica je onemogućena. Unesite PUK kôd da nastavite. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva. Za više informacija kontaktirajte mobilnog operatera.</item>
</plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
index e511940..b6dc72b 100644
--- a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Device hasn\'t been unlocked for <xliff:g id="NUMBER_0">%d</xliff:g> hour. Confirm password.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Not recognised"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
+ <item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact operator for details.</item>
+ <item quantity="one">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_0">%d</xliff:g> remaining attempt before SIM becomes permanently unusable. Contact operator for details.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
index e511940..b6dc72b 100644
--- a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Device hasn\'t been unlocked for <xliff:g id="NUMBER_0">%d</xliff:g> hour. Confirm password.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Not recognised"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
+ <item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact operator for details.</item>
+ <item quantity="one">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_0">%d</xliff:g> remaining attempt before SIM becomes permanently unusable. Contact operator for details.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
index e511940..b6dc72b 100644
--- a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Device hasn\'t been unlocked for <xliff:g id="NUMBER_0">%d</xliff:g> hour. Confirm password.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Not recognised"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
+ <item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact operator for details.</item>
+ <item quantity="one">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_0">%d</xliff:g> remaining attempt before SIM becomes permanently unusable. Contact operator for details.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
index e511940..b6dc72b 100644
--- a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Device hasn\'t been unlocked for <xliff:g id="NUMBER_0">%d</xliff:g> hour. Confirm password.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Not recognised"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
+ <item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact operator for details.</item>
+ <item quantity="one">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_0">%d</xliff:g> remaining attempt before SIM becomes permanently unusable. Contact operator for details.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
index dede142..97d16c6 100644
--- a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Device hasn\'t been unlocked for <xliff:g id="NUMBER_0">%d</xliff:g> hour. Confirm password.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Not recognized"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Enter SIM PIN, you have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
+ <item quantity="one">Enter SIM PIN, you have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your carrier to unlock your device.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact carrier for details.</item>
+ <item quantity="one">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_0">%d</xliff:g> remaining attempt before SIM becomes permanently unusable. Contact carrier for details.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml
index 001f90c..813377a 100644
--- a/packages/SystemUI/res-keyguard/values-it/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-it/strings.xml
@@ -140,7 +140,7 @@
<item quantity="one">Inserisci il codice PIN della SIM. Hai ancora <xliff:g id="NUMBER_0">%d</xliff:g> tentativo a disposizione, dopodiché dovrai contattare l\'operatore per sbloccare il dispositivo.</item>
</plurals>
<plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
- <item quantity="other">La scheda SIM è ora disattivata. Inserisci il codice PUK per continuare. Hai ancora <xliff:g id="_NUMBER_1">%d</xliff:g> tentativi a disposizione prima che la SIM diventi definitivamente inutilizzabile. Contatta l\'operatore per avere informazioni dettagliate.</item>
- <item quantity="one">La scheda SIM è ora disattivata. Inserisci il codice PUK per continuare. Hai ancora <xliff:g id="_NUMBER_0">%d</xliff:g> tentativo a disposizione prima che la SIM diventi definitivamente inutilizzabile. Contatta l\'operatore per avere informazioni dettagliate.</item>
+ <item quantity="other">La scheda SIM è ora disattivata. Inserisci il codice PUK per continuare. Hai ancora <xliff:g id="_NUMBER_1">%d</xliff:g> tentativi a disposizione prima che la SIM diventi definitivamente inutilizzabile. Per informazioni dettagliate, contatta l\'operatore.</item>
+ <item quantity="one">La scheda SIM è ora disattivata. Inserisci il codice PUK per continuare. Hai ancora <xliff:g id="_NUMBER_0">%d</xliff:g> tentativo a disposizione prima che la SIM diventi definitivamente inutilizzabile. Per informazioni dettagliate, contatta l\'operatore.</item>
</plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-my/strings.xml b/packages/SystemUI/res-keyguard/values-my/strings.xml
index 6534fbb..fb85589 100644
--- a/packages/SystemUI/res-keyguard/values-my/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-my/strings.xml
@@ -136,11 +136,11 @@
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"မသိပါ"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
- <item quantity="other">ဆင်းမ်ကဒ် ပင်နံပါတ် ထည့်သွင်းပါ၊ သင့်တွင် <xliff:g id="NUMBER_1">%d</xliff:g> ခါ ကြိုးစားခွင့် ကျန်ပါသေးသည်။</item>
- <item quantity="one">ဆင်းမ်ကဒ် ပင်နံပါတ် ထည့်သွင်းပါ၊ သင့်စက်ကို ဖွင့်ရန် ဝန်ဆောင်မှုပေးသူသို့ မဆက်သွယ်မီ သင့်တွင် <xliff:g id="NUMBER_0">%d</xliff:g> ခါ ကြိုးစားခွင့် ကျန်ပါသေးသည်။</item>
+ <item quantity="other">ဆင်းမ်ကဒ် ပင်နံပါတ် ထည့်သွင်းပါ၊ သင့်တွင် <xliff:g id="NUMBER_1">%d</xliff:g> ကြိမ် စမ်းသပ်ခွင့် ကျန်ပါသေးသည်။</item>
+ <item quantity="one">ဆင်းမ်ကဒ် ပင်နံပါတ် ထည့်သွင်းပါ၊ သင့်စက်ကို လော့ခ်ဖွင့်ပေးရန်အတွက် ဝန်ဆောင်မှုပေးသူသို့ မဆက်သွယ်မီ <xliff:g id="NUMBER_0">%d</xliff:g> ကြိမ် စမ်းသပ်ခွင့် ကျန်ပါသေးသည်။</item>
</plurals>
<plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
- <item quantity="other">ဆင်းမ်ကဒ်သည် ယခု ပိတ်သွားပါပြီ။ ရှေ့ဆက်ရန် ပင်နံပါတ် ပြန်ဖွင့်သည့် ကုဒ်ကို ထည့်ပါ။ ဆင်းမ်ကဒ်ကို အပြီးသုံးမရအောင်မပြုမီ သင့်တွင် <xliff:g id="_NUMBER_1">%d</xliff:g> ခါ ကြိုးစားခွင့်ကျန်ပါသေးသည်။ အသေးစိတ်အချက်များအတွက် ဝန်ဆောင်မှုပေးသူကို ဆက်သွယ်ပါ။</item>
- <item quantity="one">ဆင်းမ်ကဒ်သည် ယခု ပိတ်သွားပါပြီ။ ရှေ့ဆက်ရန် ပင်နံပါတ် ပြန်ဖွင့်သည့် ကုဒ်ကို ထည့်ပါ။ ဆင်းမ်ကဒ်ကို အပြီးသုံးမရအောင်မပြုမီ သင့်တွင် <xliff:g id="_NUMBER_0">%d</xliff:g> ခါ ကြိုးစားခွင့်ကျန်ပါသေးသည်။ အသေးစိတ်အချက်များအတွက် ဝန်ဆောင်မှုပေးသူကို ဆက်သွယ်ပါ။</item>
+ <item quantity="other">ဆင်းမ်ကဒ်သည် ယခု ပိတ်သွားပါပြီ။ ရှေ့ဆက်ရန် PUK ကုဒ်ကို ထည့်ပါ။ ဆင်းမ်ကဒ် အပြီးပိတ်မသွားမီ သင့်တွင် <xliff:g id="_NUMBER_1">%d</xliff:g> ကြိမ် စမ်းသပ်ခွင့် ကျန်ပါသေးသည်။ အသေးစိတ်အချက်များအတွက် ဝန်ဆောင်မှုပေးသူကို ဆက်သွယ်ပါ။</item>
+ <item quantity="one">ဆင်းမ်ကဒ်သည် ယခု ပိတ်သွားပါပြီ။ ရှေ့ဆက်ရန် PUK ကုဒ်ကို ထည့်ပါ။ ဆင်းမ်ကဒ် အပြီးပိတ်မသွားမီ သင့်တွင် <xliff:g id="_NUMBER_0">%d</xliff:g> ကြိမ် စမ်းသပ်ခွင့် ကျန်ပါသေးသည်။ အသေးစိတ်အချက်များအတွက် ဝန်ဆောင်မှုပေးသူကို ဆက်သွယ်ပါ။</item>
</plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-nl/strings.xml b/packages/SystemUI/res-keyguard/values-nl/strings.xml
index 85e0a95..6aa66dd 100644
--- a/packages/SystemUI/res-keyguard/values-nl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nl/strings.xml
@@ -50,7 +50,7 @@
<string name="keyguard_accessibility_pin_area" msgid="703175752097279029">"Gebied voor pincode"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="912702510825058921">"Gebied voor pincode van simkaart"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="136979425761438705">"Gebied voor pukcode van simkaart"</string>
- <string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Volgende alarm ingesteld voor <xliff:g id="ALARM">%1$s</xliff:g>"</string>
+ <string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Volgende wekker ingesteld voor <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Delete"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Simkaart uitschakelen"</string>
<string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
diff --git a/packages/SystemUI/res/drawable/ic_face_unlock.xml b/packages/SystemUI/res/drawable/ic_face_unlock.xml
new file mode 100644
index 0000000..29c2275
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_face_unlock.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ 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
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path android:fillColor="?attr/wallpaperTextColor"
+ android:strokeColor="?attr/wallpaperTextColor"
+ android:strokeWidth="1"
+ android:pathData="M9,11.75C8.31,11.75 7.75,12.31 7.75,13C7.75,13.69 8.31,14.25 9,14.25C9.69,14.25 10.25,13.69 10.25,13C10.25,12.31 9.69,11.75 9,11.75ZM15,11.75C14.31,11.75 13.75,12.31 13.75,13C13.75,13.69 14.31,14.25 15,14.25C15.69,14.25 16.25,13.69 16.25,13C16.25,12.31 15.69,11.75 15,11.75ZM12,2C6.48,2 2,6.48 2,12C2,17.52 6.48,22 12,22C17.52,22 22,17.52 22,12C22,6.48 17.52,2 12,2ZM12,20C7.59,20 4,16.41 4,12C4,11.71 4.02,11.42 4.05,11.14C6.41,10.09 8.28,8.16 9.26,5.77C11.07,8.33 14.05,10 17.42,10C18.2,10 18.95,9.91 19.67,9.74C19.88,10.45 20,11.21 20,12C20,16.41 16.41,20 12,20Z"
+ />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_info.xml b/packages/SystemUI/res/drawable/ic_info.xml
new file mode 100644
index 0000000..1eb4004
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_info.xml
@@ -0,0 +1,25 @@
+<!--
+ 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
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M22,34h4L26,22h-4v12zM24,4C12.95,4 4,12.95 4,24s8.95,20 20,20 20,-8.95 20,-20S35.05,4 24,4zM24,40c-8.82,0 -16,-7.18 -16,-16S15.18,8 24,8s16,7.18 16,16 -7.18,16 -16,16zM22,18h4v-4h-4v4z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml
new file mode 100644
index 0000000..255e377
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 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.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="102"
+ android:viewportWidth="102"
+ android:tint="?attr/singleToneColor">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_0_G" android:translateX="53.086" android:translateY="48.907000000000004" android:pivotX="-2.083" android:pivotY="2.083" android:rotation="90">
+ <group android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0" android:rotation="100.1" android:scaleX="0.7979999999999999" android:scaleY="0.7979999999999999">
+ <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M17.15 -37.84 C30.19,-31.91 39.52,-19.62 41.9,-4.86 C42.15,-3.39 43.45,-2.31 44.95,-2.31 C46.88,-2.31 48.34,-4.06 48.05,-5.94 C44.37,-27.64 27.64,-45.91 0.84,-48.09 C-1.08,-48.25 -2.17,-45.91 -0.83,-44.53 C-0.83,-44.53 9.87,-33.83 9.87,-33.83 C10.67,-33.04 11.92,-33.04 12.76,-33.79 C12.76,-33.79 17.15,-37.84 17.15,-37.84c "/>
+ </group>
+ <group android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0" android:rotation="87.2" android:scaleX="0.77" android:scaleY="0.77">
+ <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-21.32 42.01 C-34.36,36.07 -43.68,23.78 -46.07,9.02 C-46.33,7.55 -47.62,6.47 -49.12,6.47 C-51.04,6.47 -52.51,8.23 -52.21,10.11 C-48.53,31.81 -31.81,50.08 -5.01,52.25 C-3.09,52.42 -2,50.08 -3.34,48.7 C-3.34,48.7 -14.04,38 -14.04,38 C-14.84,37.21 -16.11,37.19 -16.93,37.96 C-16.93,37.96 -21.32,42.01 -21.32,42.01c "/>
+ </group>
+ <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M40.77 9.4 C40.77,9.4 -9.4,-40.77 -9.4,-40.77 C-11.91,-43.28 -15.67,-43.28 -18.18,-40.77 C-18.18,-40.77 -44.94,-14.01 -44.94,-14.01 C-47.45,-11.5 -47.45,-7.74 -44.94,-5.23 C-44.94,-5.23 5.23,44.94 5.23,44.94 C7.74,47.45 11.51,47.45 14.01,44.94 C14.01,44.94 40.77,18.18 40.77,18.18 C43.28,15.67 43.28,11.91 40.77,9.4c M3.85 34.82 C3.85,34.82 -34.4,-3.44 -34.4,-3.44 C-34.4,-3.44 -7.64,-30.19 -7.64,-30.19 C-7.64,-30.19 30.61,8.06 30.61,8.06 C30.61,8.06 3.85,34.82 3.85,34.82c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+
+ <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="rotation" android:duration="333" android:startOffset="0" android:valueFrom="100.1" android:valueTo="0" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+
+ <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="333" android:startOffset="0" android:valueFrom="0.798" android:valueTo="1" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.667,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="333" android:startOffset="0" android:valueFrom="0.798" android:valueTo="1" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.667,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+
+ <target android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="rotation" android:duration="333" android:startOffset="0" android:valueFrom="87.2" android:valueTo="0" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+
+ <target android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX" android:duration="333" android:startOffset="0" android:valueFrom="0.77" android:valueTo="1" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.667,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY" android:duration="333" android:startOffset="0" android:valueFrom="0.77" android:valueTo="1" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.667,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+
+ <target android:name="_R_G_L_0_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="rotation" android:duration="333" android:startOffset="0" android:valueFrom="90" android:valueTo="0" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX" android:duration="517" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_background_primary.xml b/packages/SystemUI/res/drawable/qs_background_primary.xml
index 03bba53..dd74cadd 100644
--- a/packages/SystemUI/res/drawable/qs_background_primary.xml
+++ b/packages/SystemUI/res/drawable/qs_background_primary.xml
@@ -16,5 +16,6 @@
<inset xmlns:android="http://schemas.android.com/apk/res/android">
<shape>
<solid android:color="@color/qs_background_dark"/>
+ <corners android:radius="?android:attr/dialogCornerRadius" />
</shape>
</inset>
diff --git a/core/res/res/drawable/messaging_message_background.xml b/packages/SystemUI/res/drawable/smart_reply_button_background.xml
similarity index 63%
copy from core/res/res/drawable/messaging_message_background.xml
copy to packages/SystemUI/res/drawable/smart_reply_button_background.xml
index 8a2096a..1cd1451 100644
--- a/core/res/res/drawable/messaging_message_background.xml
+++ b/packages/SystemUI/res/drawable/smart_reply_button_background.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+
<!--
~ Copyright (C) 2017 The Android Open Source Project
~
@@ -15,12 +16,12 @@
~ limitations under the License
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle"
- android:tint="#14000000">
- <corners android:radius="4dp" />
- <padding android:bottom="6dp"
- android:left="8dp"
- android:right="8dp"
- android:top="6dp" />
-</shape>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/notification_ripple_untinted_color">
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/smart_reply_button_corner_radius"/>
+ <solid android:color="@color/smart_reply_button_background"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 2fd4df4..d0d379c 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -40,6 +40,7 @@
android:gravity="center_horizontal"
android:textStyle="italic"
android:textColor="?attr/wallpaperTextColorSecondary"
+ android:textSize="16sp"
android:textAppearance="?android:attr/textAppearanceSmall"
android:visibility="gone" />
@@ -50,6 +51,7 @@
android:gravity="center_horizontal"
android:textStyle="italic"
android:textColor="?attr/wallpaperTextColorSecondary"
+ android:textSize="16sp"
android:textAppearance="?android:attr/textAppearanceSmall"
android:accessibilityLiveRegion="polite" />
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 2e35d86..6b5bd12 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -23,23 +23,22 @@
android:clickable="true"
android:orientation="vertical"
android:paddingStart="@*android:dimen/notification_content_margin_start"
+ android:paddingEnd="@*android:dimen/notification_content_margin_end"
android:background="@color/notification_guts_bg_color"
android:theme="@*android:style/Theme.DeviceDefault.Light">
<!-- Package Info -->
- <LinearLayout
+ <RelativeLayout
android:layout_width="match_parent"
- android:layout_height="@*android:dimen/notification_header_height"
+ android:layout_height="@dimen/notification_guts_header_height"
android:clipChildren="false"
- android:paddingTop="@*android:dimen/notification_header_padding_top"
- android:paddingBottom="@*android:dimen/notification_header_padding_bottom"
- android:gravity="center_vertical"
- android:orientation="horizontal" >
+ android:clipToPadding="false"
+ android:layout_marginTop="@*android:dimen/notification_header_padding_top" >
<ImageView
android:id="@+id/pkgicon"
- android:layout_width="@*android:dimen/notification_header_icon_size"
- android:layout_height="@*android:dimen/notification_header_icon_size"
- android:layout_marginEnd="3dp"/>
+ android:layout_width="@dimen/notification_guts_header_height"
+ android:layout_height="@dimen/notification_guts_header_height"
+ android:layout_marginEnd="3dp" />
<TextView
android:id="@+id/pkgname"
android:layout_width="wrap_content"
@@ -47,7 +46,9 @@
android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info"
android:layout_marginStart="3dp"
android:layout_marginEnd="2dp"
- android:singleLine="true"/>
+ android:singleLine="true"
+ android:layout_centerVertical="true"
+ android:layout_toEndOf="@id/pkgicon" />
<TextView
android:id="@+id/pkg_group_divider"
android:layout_width="wrap_content"
@@ -55,7 +56,9 @@
android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
- android:text="@*android:string/notification_header_divider_symbol"/>
+ android:text="@*android:string/notification_header_divider_symbol"
+ android:layout_centerVertical="true"
+ android:layout_toEndOf="@id/pkgname" />
<TextView
android:id="@+id/group_name"
android:layout_width="wrap_content"
@@ -64,82 +67,103 @@
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:ellipsize="end"
- android:maxLines="1"/>
- </LinearLayout>
+ android:maxLines="1"
+ android:layout_centerVertical="true"
+ android:layout_toEndOf="@id/pkg_group_divider" />
+ <ImageButton
+ android:id="@+id/info"
+ android:src="@drawable/ic_info"
+ android:tint="?android:attr/colorAccent"
+ android:layout_width="@dimen/notification_guts_header_height"
+ android:layout_height="@dimen/notification_guts_header_height"
+ android:contentDescription="@string/notification_more_settings"
+ android:background="@drawable/ripple_drawable"
+ android:layout_alignParentEnd="true" />
+ </RelativeLayout>
- <!-- Channel Info Block -->
<LinearLayout
+ android:id="@+id/prompt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="20dp"
- android:layout_marginEnd="@*android:dimen/notification_content_margin_end"
+ android:layout_marginBottom="@dimen/notification_guts_button_spacing"
+ android:layout_marginTop="@*android:dimen/notification_header_padding_top"
android:orientation="vertical">
- <!-- Channel Text -->
+
+ <!-- Channel Info Block -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal">
+ android:orientation="vertical">
<!-- Channel Name -->
<TextView
android:id="@+id/channel_name"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginBottom="6dp"
style="@style/TextAppearance.NotificationInfo.Primary" />
- <!-- Ban Channel Switch -->
- <Switch
- android:id="@+id/channel_enabled_switch"
+ <!-- Question prompt -->
+ <TextView
+ android:id="@+id/block_prompt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="end|center_vertical"
- android:contentDescription="@string/notification_channel_switch_accessibility"
- android:background="@null" />
+ style="@style/TextAppearance.NotificationInfo.Secondary" />
</LinearLayout>
- <!-- Secondary Text - only one shows at a time -->
- <TextView
- android:id="@+id/channel_disabled"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/notification_channel_disabled"
- style="@style/TextAppearance.NotificationInfo.Secondary.Warning" />
- <TextView
- android:id="@+id/num_channels_desc"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/notification_channel_disabled"
- style="@style/TextAppearance.NotificationInfo.Secondary" />
- <!-- Optional link to app. Only appears if the channel is not disabled -->
- <TextView
- android:id="@+id/app_settings"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- android:ellipsize="end"
- android:maxLines="1"
- style="@style/TextAppearance.NotificationInfo.Secondary.Link"/>
- </LinearLayout>
- <!-- Settings and Done buttons -->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="48dp"
- android:orientation="horizontal"
- android:gravity="end"
- android:layout_marginEnd="@*android:dimen/notification_content_margin_end"
- android:layout_marginBottom="8dp" >
- <TextView
- android:id="@+id/more_settings"
- android:text="@string/notification_more_settings"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginEnd="8dp"
- style="@style/TextAppearance.NotificationInfo.Button"/>
- <TextView
- android:id="@+id/done"
- android:text="@string/notification_done"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- style="@style/TextAppearance.NotificationInfo.Button"/>
+ <!-- Settings and Done buttons -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="@dimen/notification_guts_button_spacing"
+ android:gravity="end" >
+
+ <!-- Optional link to app. Only appears if the channel is not disabled and the app
+ asked for it -->
+ <TextView
+ android:id="@+id/app_settings"
+ android:text="@string/notification_app_settings"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:ellipsize="end"
+ android:maxLines="1"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
+ android:id="@+id/block"
+ android:text="@string/inline_stop_button"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
+ android:id="@+id/keep"
+ android:text="@string/inline_keep_button"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="-8dp"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ </LinearLayout>
</LinearLayout>
+ <RelativeLayout
+ android:id="@+id/confirmation"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/notification_guts_button_spacing"
+ android:layout_marginTop="@*android:dimen/notification_header_padding_top"
+ android:visibility="gone"
+ android:orientation="horizontal" >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/notification_channel_disabled"
+ style="@style/TextAppearance.NotificationInfo.Secondary.Warning"/>
+ <TextView
+ android:id="@+id/undo"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/inline_undo"
+ android:layout_alignParentEnd="true"
+ android:layout_marginEnd="-8dp"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ </RelativeLayout>
</com.android.systemui.statusbar.NotificationInfo>
diff --git a/packages/SystemUI/res/layout/rotate_suggestion.xml b/packages/SystemUI/res/layout/rotate_suggestion.xml
new file mode 100644
index 0000000..7762950
--- /dev/null
+++ b/packages/SystemUI/res/layout/rotate_suggestion.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:layout_width="@dimen/navigation_side_padding"
+ android:layout_height="match_parent"
+ android:layout_weight="0"
+ >
+ <com.android.systemui.statusbar.policy.KeyButtonView
+ android:id="@+id/rotate_suggestion"
+ android:layout_width="@dimen/navigation_extra_key_width"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="2dp"
+ android:visibility="invisible"
+ android:scaleType="centerInside"
+ />
+ <!-- TODO android:contentDescription -->
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/smart_reply_button.xml b/packages/SystemUI/res/layout/smart_reply_button.xml
new file mode 100644
index 0000000..4ac41d5
--- /dev/null
+++ b/packages/SystemUI/res/layout/smart_reply_button.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2017 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
+ -->
+
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@android:style/Widget.Material.Button.Borderless.Small"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/smart_reply_button_spacing"
+ android:paddingVertical="@dimen/smart_reply_button_padding_vertical"
+ android:paddingHorizontal="@dimen/smart_reply_button_corner_radius"
+ android:background="@drawable/smart_reply_button_background"
+ android:gravity="center"
+ android:fontFamily="sans-serif"
+ android:textSize="@dimen/smart_reply_button_font_size"
+ android:textColor="@color/smart_reply_button_text"
+ android:textStyle="normal"
+ android:singleLine="true"/>
\ No newline at end of file
diff --git a/core/res/res/drawable/messaging_message_background.xml b/packages/SystemUI/res/layout/smart_reply_view.xml
similarity index 62%
copy from core/res/res/drawable/messaging_message_background.xml
copy to packages/SystemUI/res/layout/smart_reply_view.xml
index 8a2096a..6d53386 100644
--- a/core/res/res/drawable/messaging_message_background.xml
+++ b/packages/SystemUI/res/layout/smart_reply_view.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+
<!--
~ Copyright (C) 2017 The Android Open Source Project
~
@@ -15,12 +16,12 @@
~ limitations under the License
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle"
- android:tint="#14000000">
- <corners android:radius="4dp" />
- <padding android:bottom="6dp"
- android:left="8dp"
- android:right="8dp"
- android:top="6dp" />
-</shape>
+<!-- LinearLayout -->
+<com.android.systemui.statusbar.policy.SmartReplyView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/smart_reply_view"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="end">
+ <!-- smart_reply_button(s) will be added here. -->
+</com.android.systemui.statusbar.policy.SmartReplyView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index bef0830..c5e5ee1 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -43,6 +43,8 @@
android:layout_width="@dimen/qs_panel_width"
android:layout_height="match_parent"
android:layout_gravity="@integer/notification_panel_layout_gravity"
+ android:layout_marginStart="@dimen/notification_side_paddings"
+ android:layout_marginEnd="@dimen/notification_side_paddings"
android:clipToPadding="false"
android:clipChildren="false"
systemui:viewType="com.android.systemui.plugins.qs.QS" />
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 7f37087..4614999 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -54,6 +54,18 @@
android:paddingStart="8dp"
/>
+ <ImageButton
+ android:id="@+id/helper"
+ android:layout_width="48dp"
+ android:layout_height="@*android:dimen/notification_header_height"
+ android:layout_gravity="top|end"
+ android:layout_marginEnd="6dp"
+ android:src="@drawable/ic_dnd"
+ android:tint="#FF0000"
+ android:background="@drawable/ripple_drawable"
+ android:visibility="visible"
+ />
+
<ViewStub
android:layout="@layout/notification_children_container"
android:id="@+id/child_container_stub"
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index f0d2346..748c9a5 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -17,130 +17,76 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:background="@android:color/transparent"
android:theme="@style/qs_theme"
android:clipChildren="false" >
- <RelativeLayout
+ <LinearLayout
android:id="@+id/volume_dialog"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingTop="@dimen/volume_row_padding_bottom"
- android:background="@drawable/rounded_full_bg_bottom"
+ android:layout_gravity="center_vertical|end"
+ android:minWidth="@dimen/volume_dialog_panel_width"
+ android:background="@android:color/transparent"
+ android:layout_margin="12dp"
android:translationZ="8dp"
+ android:orientation="vertical"
android:clipChildren="false" >
<LinearLayout
- android:id="@+id/volume_dialog_content"
- android:layout_width="match_parent"
+ android:id="@+id/volume_dialog_rows"
+ android:layout_width="@dimen/volume_dialog_panel_width"
android:layout_height="wrap_content"
- android:layout_toStartOf="@id/expand"
android:clipChildren="false"
android:clipToPadding="false"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp"
+ android:background="@drawable/rounded_bg_full"
+ android:orientation="horizontal" >
+ <!-- volume rows added and removed here! :-) -->
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/footer"
+ android:layout_width="@dimen/volume_dialog_panel_width"
+ android:layout_height="@dimen/volume_dialog_panel_width"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:layout_marginTop="6dp"
+ android:layout_marginBottom="6dp"
+ android:layout_below="@id/volume_dialog_rows"
+ android:background="@drawable/rounded_bg_full"
+ android:gravity="center"
android:orientation="vertical" >
- <LinearLayout
- android:id="@+id/volume_dialog_rows"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
- <!-- volume rows added and removed here! :-) -->
- </LinearLayout>
-
-
- </LinearLayout>
- <LinearLayout
- android:id="@+id/expand"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_alignParentEnd="true"
- android:layout_alignParentTop="true"
- android:layout_marginEnd="@dimen/volume_expander_margin_end" >
<TextView
+ android:id="@+id/ringer_title"
+ android:text="@string/ring_toggle_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
- android:textAppearance="@style/TextAppearance.Volume.Header" />
+ android:layout_centerVertical="true"
+ android:textColor="?android:attr/colorControlNormal"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
<com.android.keyguard.AlphaOptimizedImageButton
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/volume_expand_button"
- style="@style/VolumeButtons"
- android:layout_width="@dimen/volume_button_size"
- android:layout_height="@dimen/volume_button_size"
- android:clickable="true"
- android:soundEffectsEnabled="false"
- android:src="@drawable/ic_volume_expand_animation"
- android:background="@drawable/ripple_drawable"
- tools:ignore="RtlHardcoded" />
- </LinearLayout>
- <RelativeLayout
- android:id="@+id/footer"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:layout_below="@id/volume_dialog_content"
- android:layout_margin="10dp">
- <!-- special row for ringer mode -->
- <RelativeLayout
- android:id="@+id/ringer_mode"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@drawable/rounded_bg_full"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:layout_toStartOf="@id/output_chooser"
- android:layout_margin="10dp">
-
- <com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/ringer_icon"
- style="@style/VolumeButtons"
- android:background="?android:selectableItemBackgroundBorderless"
- android:layout_width="@dimen/volume_button_size"
- android:layout_height="@dimen/volume_button_size"
- android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- android:soundEffectsEnabled="false" />
-
- <TextView
- android:id="@+id/ringer_title"
- android:text="@string/ring_toggle_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:maxLines="1"
- android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- android:layout_toEndOf="@+id/ringer_icon"
- android:layout_marginStart="64dp"
- android:textColor="?android:attr/colorControlNormal"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:paddingStart="@dimen/volume_row_header_padding_start" />
-
- <TextView
- android:id="@+id/ringer_status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
- android:layout_marginEnd="14dp"
- android:maxLines="1"
- android:textColor="?android:attr/colorControlNormal"
- android:textAppearance="?android:attr/textAppearanceSmall" />
-
- </RelativeLayout>
- <com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/output_chooser"
+ android:id="@+id/ringer_icon"
style="@style/VolumeButtons"
android:background="?android:selectableItemBackgroundBorderless"
android:layout_width="@dimen/volume_button_size"
android:layout_height="@dimen/volume_button_size"
- android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
- android:src="@drawable/ic_settings_bluetooth"
+ android:tint="?android:attr/colorAccent"
android:soundEffectsEnabled="false" />
- </RelativeLayout>
- </RelativeLayout>
+
+ <TextView
+ android:id="@+id/ringer_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/colorControlNormal"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ </LinearLayout>
+ </LinearLayout>
</com.android.systemui.volume.VolumeUiLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index bf76e78..3590b76 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -15,48 +15,70 @@
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/volume_row_height"
- android:clipChildren="false"
- android:clipToPadding="false"
+ android:tag="row"
+ android:layout_height="wrap_content"
+ android:layout_width="@dimen/volume_dialog_panel_width"
+ android:clipChildren="true"
+ android:clipToPadding="true"
android:theme="@style/qs_theme"
+ android:gravity="center"
android:orientation="vertical" >
- <TextView
- android:id="@+id/volume_row_header"
+ <LinearLayout
+ android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:ellipsize="end"
- android:maxLines="1"
- android:textColor="?android:attr/colorControlNormal"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:paddingStart="@dimen/volume_row_header_padding_start" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="@dimen/volume_row_slider_height"
- android:orientation="horizontal"
- android:paddingStart="@dimen/volume_row_padding_start" >
+ android:gravity="center"
+ android:padding="10dp">
+ <TextView
+ android:id="@+id/volume_row_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/colorControlNormal"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ <TextView
+ android:id="@+id/volume_row_connected_device"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textAppearance="@style/TextAppearance.QS.DetailItemSecondary" />
<com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/volume_row_icon"
- style="@style/VolumeButtons"
- android:layout_width="@dimen/volume_button_size"
- android:layout_height="@dimen/volume_button_size"
- android:soundEffectsEnabled="false" />
-
- <SeekBar
- android:id="@+id/volume_row_slider"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alignWithParentIfMissing="true"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:paddingStart="@dimen/volume_row_slider_padding_start"/>
+ android:id="@+id/output_chooser"
+ style="@style/VolumeButtons"
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:layout_width="@dimen/volume_button_size"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:src="@drawable/ic_volume_expand_animation"
+ android:soundEffectsEnabled="false" />
</LinearLayout>
+ <FrameLayout
+ android:id="@+id/volume_row_slider_frame"
+ android:padding="10dp"
+ android:layout_width="@dimen/volume_dialog_panel_width"
+ android:layout_height="150dp">
+ <SeekBar
+ android:id="@+id/volume_row_slider"
+ android:padding="0dp"
+ android:layout_margin="0dp"
+ android:layout_width="150dp"
+ android:layout_height="@dimen/volume_dialog_panel_width"
+ android:layout_gravity="center"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:rotation="270" />
+ </FrameLayout>
- <Space
- android:id="@+id/spacer"
- android:layout_width="match_parent"
- android:layout_height="@dimen/volume_row_padding_bottom"/>
+ <com.android.keyguard.AlphaOptimizedImageButton
+ android:id="@+id/volume_row_icon"
+ style="@style/VolumeButtons"
+ android:padding="10dp"
+ android:layout_width="@dimen/volume_button_size"
+ android:layout_height="@dimen/volume_button_size"
+ android:soundEffectsEnabled="false" />
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 15a7d4c..fb756e3 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Tik om te demp."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s volumekontroles word gewys. Swiep na bo om toe te maak."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Volumekontroles is versteek"</string>
+ <string name="output_title" msgid="5355078100792942802">"Media-uitvoer"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Foonoproep-uitvoer"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Geen toestelle gekry nie"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Geen toestelle gekry nie. Probeer om <xliff:g id="SERVICE">%1$s</xliff:g> aan te skakel"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth en Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Stelsel-UI-ontvanger"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Wys persentasie van ingebedde battery"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Wys batteryvlakpersentasie binne die statusbalkikoon wanneer dit nie laai nie"</string>
@@ -724,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Vou uit"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimeer"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Maak toe"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Sleep af om toe te maak"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Kieslys"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> is in beeld-in-beeld"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index ee9acae..e548cae 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -33,7 +33,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"በመካሄድ ላይ ያለ"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"ማሳወቂያዎች"</string>
<string name="battery_low_title" msgid="6456385927409742437">"የባትሪ ኃይል አነስተኛ ነው"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"ባትሪ ዝቅተኛ ነው። የባትሪ ቆጣቢን ያብሩ"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> ይቀራል"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> ይቀራል፣ በእርስዎ አጠቃቀም ላይ በመመረት <xliff:g id="TIME">%s</xliff:g> ገደማ ይቀራል"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> ይቀራል፣ <xliff:g id="TIME">%s</xliff:g> ገደማ ይቀራል"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> ይቀራል። ባትሪ ቆጣቢ በርቷል።"</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB ኃይል መሙያ አይታገዝም።\n የቀረበውን ኃይል መሙያ ብቻ ተጠቀም።"</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"የUSB ኃይል መሙላት አይደገፍም።"</string>
@@ -67,14 +70,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"አሁን ወደዚህ መሣሪያ የገባው ተጠቃሚ የዩኤስቢ እርማትን ማብራት አይችልም። ይህን ባህሪ ለመጠቀም ወደ ዋና ተጠቃሚ ይቀይሩ።"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"ማያ እንዲሞላ አጉላ"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"ማያ ለመሙለት ሳብ"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"ቅጽበታዊ ገጽ እይታ"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"ቅጽበታዊ ገጽ እይታ በማስቀመጥ ላይ..."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"ቅጽበታዊ ገጽ እይታ በማስቀመጥ ላይ..."</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"ቅጽበታዊ ገጽ እይታ እየተቀመጠ ነው::"</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"ቅጽበታዊ ገጽ እይታ ተቀርጿል"</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"የእርስዎን ቅጽበታዊ ገጽ እይታ ለማየት መታ ያድርጉ።"</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"ቅጽበታዊ ገጽ እይታ መቅረጽ አልተቻለም::"</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"ቅጽበታዊ ገጽ ዕይታን በማስቀመጥ ጊዜ ችግር አጋጥሟል።"</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"ባለው የተገደበ የማከማቻ ቦታ ምክንያት ቅጽበታዊ ገጽ ዕይታን ማስቀመጥ አይችልም።"</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"ቅጽበታዊ ገጽ እይታ እየተቀመጠ ነው"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"ቅጽበታዊ ገጽ እይታ ተቀምጧል"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"የእርስዎን ቅጽበታዊ ገጽ እይታ ለማየት መታ ያድርጉ"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"ቅጽበታዊ ገጽ እይታን ማንሳት አልተቻለም"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"ቅጽበታዊ ገጽ ዕይታን በማስቀመጥ ላይ ሳለ ችግር አጋጥሟል"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"ባለው ውሱን የማከማቻ ቦታ ምክንያት ቅጽበታዊ ገጽ ዕይታን ማስቀመጥ አይችልም"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"ቅጽበታዊ ገጽ እይታዎችን ማንሳት በመተግበሪያው ወይም በእርስዎ ድርጅት አይፈቀድም"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"የUSB ፋይል ሰደዳ አማራጮች"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"እንደ ማህደረ አጫዋች (MTP) ሰካ"</string>
@@ -503,6 +507,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s። ድምጸ-ከል ለማድረግ መታ ያድርጉ።"</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"የ%s ድምጽ መቆጣጠሪያዎች ይታያሉ። ለማሰናበት ወደ ላይ ያንሸራትቱ።"</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"የድምጽ መቆጣጠሪያዎች ተደብቀዋል"</string>
+ <string name="output_title" msgid="5355078100792942802">"የሚዲያ ውጽዓት"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"የስልክ ጥሪ ውፅዓት"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"ምንም መሣሪያዎች አልተገኙም"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"ምንም መሣሪያዎች አልተገኙም። <xliff:g id="SERVICE">%1$s</xliff:g>ን ማብራት ይሞክሩ።"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"ብሉቱዝ"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"ብሉቱዝ እና Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"የስርዓት በይነገጽ መቃኛ"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"የተቀላቀለ የባትሪ አጠቃቀም መቶኛ አሳይ"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"ኃይል በማይሞላበት ጊዜ በሁነታ አሞሌ አዶ ውስጥ የባትሪ ደረጃ መቶኛን አሳይ"</string>
@@ -550,26 +561,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ጠፍቷል"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"በኃይል ማሳወቂያ መቆጣጠሪያዎች አማካኝነት የአንድ መተግበሪያ ማሳወቂያዎች የአስፈላጊነት ደረጃ ከ0 እስከ 5 ድረስ ማዘጋጀት ይችላሉ። \n\n"<b>"ደረጃ 5"</b>" \n- በማሳወቂያ ዝርዝሩ አናት ላይ አሳይ \n- የሙሉ ማያ ገጽ ማቋረጥን ፍቀድ \n- ሁልጊዜ አጮልቀው ይመልከቱ \n\n"<b>"ደረጃ 4"</b>" \n- የሙሉ ማያ ገጽ ማቋረጥን ከልክል \n- ሁልጊዜ አጮልቀው ይመልከቱ \n\n"<b>"ደረጃ 3"</b>" \n- የሙሉ ማያ ገጽ ማቋረጥን ከልክል \n- በፍጹም አጮልቀው አይምልከቱ \n\n"<b>"ደረጃ 2"</b>" \n- የሙሉ ማያ ገጽ ማቋረጥን ይከልክሉ \n- በፍጹም አጮልቀው አይመልከቱ \n- ድምፅ እና ንዝረትን በፍጹም አይኑር \n\n"<b>"ደረጃ 1"</b>" \n- የሙሉ ማያ ገጽ ማቋረጥን ይከልክሉ \n- በፍጹም አጮልቀው አይመልከቱ \n- ድምፅ ወይም ንዝረትን በፍጹም አያደርጉ \n- ከመቆለፊያ ገጽ እና የሁኔታ አሞሌ ይደብቁ \n- በማሳወቂያ ዝርዝር ግርጌ ላይ አሳይ \n\n"<b>"ደረጃ 0"</b>" \n- ሁሉንም የመተግበሪያው ማሳወቂያዎች ያግዱ"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"ማሳወቂያዎች"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"እነዚህን ማሳወቂያዎች ከእንግዲህ አያገኟቸውም"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> የማሳወቂያ ምድቦች"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"ይህ መተግበሪያ የማሳወቂያ ምድቦች የሉትም"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"ከዚህ መተግበሪያ የሚመጡ ማሳወቂያዎች ሊጠፉ አይችሉም"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="one">1 ከ<xliff:g id="NUMBER_1">%s</xliff:g> የማሳወቂያ ምድቦች ከዚህ መተግበሪያ</item>
- <item quantity="other">1 <xliff:g id="NUMBER_1">%s</xliff:g> የማሳወቂያ ምድቦች ከዚህ መተግበሪያ</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>፣ <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>፣ <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> እና <xliff:g id="NUMBER_5">%3$d</xliff:g> ሌሎች</item>
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>፣ <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> እና <xliff:g id="NUMBER_5">%3$d</xliff:g> ሌሎች</item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"እነዚህን ማሳወቂያዎችን ከእንግዲህ አይመለከቷቸውም"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"እነዚህን ማሳወቂያዎች ማሳየት ይቀጥሉ?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"ማሳወቂያዎችን አስቁም"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"ማሳየትን ቀጥል"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"ከዚህ መተግበሪያ ማሳወቂያዎችን ማሳየት ይቀጥል?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"እነዚህ ማሳወቂያዎች ሊጠፉ አይችሉም"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"የ<xliff:g id="APP_NAME">%1$s</xliff:g> ማሳወቂያ መቆጣጠሪያዎች ተከፍተዋል"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"የ<xliff:g id="APP_NAME">%1$s</xliff:g> ማሳወቂያ መቆጣጠሪያዎች ተዘግተዋል"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"ከዚህ ሰርጥ የመጡ ሁሉንም ማሳወቂያች ፍቀድ"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"ሁሉም ምድቦች"</string>
<string name="notification_more_settings" msgid="816306283396553571">"ተጨማሪ ቅንብሮች"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"ያብጁ፦ <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"አብጅ"</string>
<string name="notification_done" msgid="5279426047273930175">"ተከናውኗል"</string>
+ <string name="inline_undo" msgid="558916737624706010">"ቀልብስ"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"የማሳወቂያ መቆጣጠሪያዎች"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"የማሳወቂያ ማሸለቢያ አማራጮች"</string>
@@ -724,6 +728,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"ዘርጋ"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"አሳንስ"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"ዝጋ"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"ቅንብሮች"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"ለማሰናበት ወደ ታች ይጎትቱ"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"ምናሌ"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> በስዕል-ላይ-ስዕል ውስጥ ነው"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 1d8cb4e..e5db8de 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -26,7 +26,7 @@
<string name="status_bar_no_recent_apps" msgid="7374907845131203189">"تظهر شاشاتك المعروضة مؤخرًا هنا"</string>
<string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"إزالة التطبيقات الحديثة"</string>
<plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
- <item quantity="zero"> لا توجد أية شاشات (%d) في النظرة العامة</item>
+ <item quantity="zero"> لا توجد أي شاشات (%d) في النظرة العامة</item>
<item quantity="two">شاشتان (%d) في النظرة العامة</item>
<item quantity="few">%d شاشات في النظرة العامة</item>
<item quantity="many">%d شاشة في النظرة العامة</item>
@@ -302,7 +302,7 @@
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"لا تتوفر شبكة"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"إيقاف Wi-Fi"</string>
<string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"تم تشغيل Wi-Fi"</string>
- <string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"لا تتوفر أية شبكة Wi-Fi"</string>
+ <string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"لا تتوفر أي شبكة Wi-Fi"</string>
<string name="quick_settings_cast_title" msgid="7709016546426454729">"إرسال"</string>
<string name="quick_settings_casting" msgid="6601710681033353316">"جارٍ الإرسال"</string>
<string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"جهاز لا يحمل اسمًا"</string>
@@ -511,6 +511,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. انقر لكتم الصوت."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"تم عرض %s عنصر تحكم في مستوى الصوت. يمكنك التمرير سريعًا لأعلى للتجاهل."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"تم إخفاء عناصر التحكم في مستوى الصوت"</string>
+ <string name="output_title" msgid="5355078100792942802">"إخراج الوسائط"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"إخراج المكالمة الهاتفية"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"لم يتم العثور على أي أجهزة."</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"لم يتم العثور على أي أجهزة. جرِّب تفعيل <xliff:g id="SERVICE">%1$s</xliff:g>."</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"البلوتوث"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"البلوتوث وWi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"أداة ضبط واجهة مستخدم النظام"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"عرض نسبة البطارية المدمجة"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"عرض نسبة مستوى البطارية داخل رمز شريط الحالة أثناء عدم الشحن"</string>
@@ -748,6 +755,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"توسيع"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"تصغير"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"إغلاق"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"اسحب لأسفل للإلغاء"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"القائمة"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> يظهر في صورة داخل صورة"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 098b1cd..1b2972b 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -492,6 +492,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Çoxsaylı ton olan ikili tezlik"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Münasiblik"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Zənglər"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"Zəng"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrasiya"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"Susdurun"</string>
@@ -502,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Səssiz etmək üçün klikləyin."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s həcm nəzarəti göstərilir. Bitirmək üçün yuxarı çəkin."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Həcm nəzarət gizlədilib"</string>
+ <string name="output_title" msgid="5355078100792942802">"Media çıxışı"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Zəng girişi"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Heç bir cihaz tapılmadı"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Cihaz tapılmadı. <xliff:g id="SERVICE">%1$s</xliff:g> xidmətini aktiv edin"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth və Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Daxil batareya faizini göstərin"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Elektrik şəbəsinə qoşulu olmayan zaman batareya səviyyəsini status paneli ikonası daxilində göstərin"</string>
@@ -723,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Genişləndirin"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Kiçildin"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Bağlayın"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Rədd etmək üçün aşağı çəkin"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menyu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> şəkil içində şəkildədir"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index fd42eb5..2d3635c 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -505,6 +505,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Dodirnite da biste isključili zvuk."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Kontrole za jačinu zvuka (%s) su prikazane. Prevucite nagore da biste ih odbacili."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Kontrole za jačinu zvuka su sakrivene"</string>
+ <string name="output_title" msgid="5355078100792942802">"Izlaz medija"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Izlaz za telefonski poziv"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Nije pronađen nijedan uređaj"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Nije pronađen nijedan uređaj. Probajte da uključite uslugu <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth i Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Tjuner za korisnički interfejs sistema"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Prikazuj ugrađeni procenat baterije"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Prikazivanje nivoa napunjenosti baterije u procentima unutar ikone na statusnoj traci kada se baterija ne puni"</string>
@@ -730,6 +737,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Proširi"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Umanji"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Zatvori"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Prevucite nadole da biste odbili"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Meni"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> je slika u slici"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index e82dbff..27e3a7d 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -509,6 +509,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Дакраніцеся, каб адключыць гук"</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Паказваецца наступная колькасць рэгулятараў гучнасці: %s. Правядзіце пальцам уверх, каб закрыць іх."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Рэгулятары гучнасці схаваны"</string>
+ <string name="output_title" msgid="5355078100792942802">"Вывад мультымедыя"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Прылада вываду тэлефонных выклікаў"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Прылады не знойдзены"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Прылады не знойдзены. Паспрабуйце ўключыць <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth і Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Наладка сістэмнага інтэрфейсу карыстальніка"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Паказваць працэнт зараду акумулятара"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Паказваць працэнт узроўню акумулятара ўнутры значка панэлі стану, калі ён не зараджаецца"</string>
@@ -738,6 +745,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Разгарнуць"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Згарнуць"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Закрыць"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Перацягніце ўніз, каб адхіліць"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Меню"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> з’яўляецца відарысам у відарысе"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index ccdc44b..6a93d9e 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Докоснете, за да заглушите звука."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Показани са контролите за силата на звука на %s. Прекарайте пръст нагоре, за да ги скриете."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Контролите за силата на звука са скрити"</string>
+ <string name="output_title" msgid="5355078100792942802">"Изходяща мултимедия"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Изходящи телефонни обаждания"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Няма намерени устройства"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Няма намерени устройства. Включете <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth и Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Тунер на системния потребителски интерфейс"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Показване на процента на вградената батерия"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Показване на процента на нивото на батерията в иконата на лентата на състоянието, когато не се зарежда"</string>
@@ -724,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Разгъване"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Намаляване"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Затваряне"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Преместете надолу с плъзгане, за да отхвърлите"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Меню"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> е в режима „Картина в картината“"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 96028f7..4b163ec 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -492,8 +492,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"ব্লুটুথ"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"ডুয়েল মাল্টি টোন ফ্রিকোয়েন্সি"</string>
<string name="stream_accessibility" msgid="301136219144385106">"অ্যাক্সেসযোগ্যতা"</string>
- <!-- no translation found for ring_toggle_title (3281244519428819576) -->
- <skip />
+ <string name="ring_toggle_title" msgid="3281244519428819576">"কল"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"রিং"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"ভাইব্রেট"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"মিউট"</string>
@@ -504,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s। নিঃশব্দ করতে ট্যাপ করুন।"</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s ভলিউম নিয়ন্ত্রণগুলি দেখানো হয়েছে৷ খারিজ করতে উপরের দিকে সোয়াইপ করুন৷"</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"ভলিউম নিয়ন্ত্রণগুলি লুকানো রয়েছে"</string>
+ <string name="output_title" msgid="5355078100792942802">"মিডিয়া আউটপুট"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"ফোন কল আউটপুট"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"কোনও ডিভাইস খুঁজে পাওয়া যায়নি"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"কোনও ডিভাইস খুঁজে পাওয়া যায়নি। <xliff:g id="SERVICE">%1$s</xliff:g> চালু করার চেষ্টা করুন"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"ব্লুটুথ"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"ওয়াই-ফাই"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"ব্লুটুথ এবং ওয়াই-ফাই"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"সিস্টেম UI টিউনার"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"এম্বেড করা ব্যাটারির শতকরা হার দেখায়"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"যখন চার্জ করা হবে না তখন স্থিতি দন্ডের আইকনের ভিতরে ব্যাটারি স্তরের শতকার হার দেখায়"</string>
@@ -725,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"বড় করুন"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"ছোটো করুন"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"বন্ধ করুন"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"খারিজ করতে নিচের দিকে টেনে আনুন"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"মেনু"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"ছবির-মধ্যে-ছবি তে <xliff:g id="NAME">%s</xliff:g> আছেন"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index f09bb1f..fc246c4 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -153,9 +153,9 @@
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
<string name="accessibility_data_connection_wifi" msgid="2324496756590645221">"Wi-Fi"</string>
<string name="accessibility_no_sim" msgid="8274017118472455155">"Nema SIM kartice."</string>
- <string name="accessibility_cell_data" msgid="5326139158682385073">"Mobilni podaci"</string>
- <string name="accessibility_cell_data_on" msgid="5927098403452994422">"Mobilni podaci su uključeni"</string>
- <string name="accessibility_cell_data_off" msgid="443267573897409704">"Mobilni podaci su isključeni"</string>
+ <string name="accessibility_cell_data" msgid="5326139158682385073">"Prijenos podataka na mobilnoj mreži"</string>
+ <string name="accessibility_cell_data_on" msgid="5927098403452994422">"Prijenos podataka na mobilnoj mreži je uključen"</string>
+ <string name="accessibility_cell_data_off" msgid="443267573897409704">"Prijenos podataka na mobilnoj mreži je isključen"</string>
<string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Dijeljenje Bluetooth veze."</string>
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Način rada u avionu."</string>
<string name="accessibility_vpn_on" msgid="5993385083262856059">"VPN uključen."</string>
@@ -240,9 +240,9 @@
<string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Punjenje"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G–3G prijenos podataka je pauzirano"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G prijenos podataka je pauzirano"</string>
- <string name="data_usage_disabled_dialog_mobile_title" msgid="6801382439018099779">"Mobilni podaci su pauzirani"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="6801382439018099779">"Prijenos podataka je pauziran"</string>
<string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Prijenos podataka je pauziran"</string>
- <string name="data_usage_disabled_dialog" msgid="4919541636934603816">"Dostigli ste ograničenje za prijenos podataka koje ste postavili. Više ne koristite mobilne podatke.\n\nUkoliko nastavite koristiti mobilne podatke, mogući su troškovi za prijenos podataka."</string>
+ <string name="data_usage_disabled_dialog" msgid="4919541636934603816">"Dostigli ste ograničenje za prijenos podataka koje ste postavili. Više ne koristite prijenos podataka na mobilnoj mreži.\n\nUkoliko nastavite koristiti prijenos podataka na mobilnoj mreži, mogući su troškovi za prijenos podataka."</string>
<string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Nastavi"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Traženje GPS signala"</string>
<string name="gps_notification_found_text" msgid="4619274244146446464">"Lokacija utvrđena GPS signalom"</string>
@@ -315,7 +315,7 @@
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Pristupna tačka"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Obavještenja"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Svjetiljka"</string>
- <string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobilni podaci"</string>
+ <string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Prijenos podataka na mobilnoj mreži"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Prijenos podataka"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Preostala količina podataka"</string>
<string name="quick_settings_cellular_detail_over_limit" msgid="967669665390990427">"Prekoračeno"</string>
@@ -412,16 +412,16 @@
<string name="profile_owned_footer" msgid="8021888108553696069">"Profil može biti nadziran"</string>
<string name="vpn_footer" msgid="2388611096129106812">"Mreža može biti nadzirana"</string>
<string name="branded_vpn_footer" msgid="2168111859226496230">"Mreža može biti nadzirana"</string>
- <string name="quick_settings_disclosure_management_monitoring" msgid="6645176135063957394">"Vaša organizacija upravlja ovim uređajem i može pratiti mrežni promet."</string>
- <string name="quick_settings_disclosure_named_management_monitoring" msgid="370622174777570853">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> upravlja ovim uređajem i može pratiti vaš mrežni promet"</string>
+ <string name="quick_settings_disclosure_management_monitoring" msgid="6645176135063957394">"Vaša organizacija upravlja ovim uređajem i može pratiti mrežni saobraćaj."</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="370622174777570853">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> upravlja ovim uređajem i može pratiti vaš mrežni saobraćaj"</string>
<string name="quick_settings_disclosure_management_named_vpn" msgid="1085137869053332307">"Uređajem upravlja vaša organizacija i povezan je s aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="quick_settings_disclosure_named_management_named_vpn" msgid="6290456493852584017">"Uređajem upravlja <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> i povezan je s aplikacijom <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
<string name="quick_settings_disclosure_management" msgid="3294967280853150271">"Uređajem upravlja vaša organizacija"</string>
<string name="quick_settings_disclosure_named_management" msgid="1059403025094542908">"Ovim uređajem upravlja <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>."</string>
<string name="quick_settings_disclosure_management_vpns" msgid="3698767349925266482">"Uređajem upravlja vaša organizacija i povezan je s VPN-ovima"</string>
<string name="quick_settings_disclosure_named_management_vpns" msgid="7777821385318891527">"Uređajem upravlja <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> i povezan je s VPN-ovima"</string>
- <string name="quick_settings_disclosure_managed_profile_monitoring" msgid="5125463987558278215">"Vaša organizacija može pratiti mrežni promet na vašem profilu."</string>
- <string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8973606847896650284">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> može pratiti mrežni promet na vašem radnom profilu"</string>
+ <string name="quick_settings_disclosure_managed_profile_monitoring" msgid="5125463987558278215">"Vaša organizacija može pratiti mrežni saobraćaj na vašem profilu."</string>
+ <string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8973606847896650284">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> može pratiti mrežni saobraćaj na vašem radnom profilu"</string>
<string name="quick_settings_disclosure_monitoring" msgid="679658227269205728">"Mreža može biti nadzirana"</string>
<string name="quick_settings_disclosure_vpns" msgid="8170318392053156330">"Uređaj je povezan s VPN-ovima"</string>
<string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="3494535754792751741">"Radni profil je povezan s aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
@@ -438,10 +438,10 @@
<string name="monitoring_button_view_policies" msgid="100913612638514424">"Prikaži pravila"</string>
<string name="monitoring_description_named_management" msgid="5281789135578986303">"Vašim uređajem upravlja organizacija <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nVaš administrator može nadgledati i upravljati vašim postavkama, korporativnom pristupu, aplikacijama, podacima koji su povezani s vašim uređajem i informacijama o lokaciji vašeg uređaja.\n\nZa više informacija, obratite se svom administratoru."</string>
<string name="monitoring_description_management" msgid="4573721970278370790">"Vašim uređajem upravlja vaša organizacija.\n\nVaš administrator može nadgledati i upravljati vašim postavkama, korporativnom pristupu, aplikacijama, podacima koji su povezani s vašim uređajem i informacijama o lokaciji vašeg uređaja.\n\nZa više informacija, obratite se svom administratoru."</string>
- <string name="monitoring_description_management_ca_certificate" msgid="5202023784131001751">"Vaša organizacija je instalirala CA certifikat na ovom uređaju. Vaš promet preko sigurne mreže može se pratiti."</string>
- <string name="monitoring_description_managed_profile_ca_certificate" msgid="4683248196789897964">"Vaša organizacija je instalirala CA certifikat na vašem radnom profilu. Vaš promet preko sigurne mreže može se pratiti."</string>
- <string name="monitoring_description_ca_certificate" msgid="7886985418413598352">"CA certifikat je instaliran na ovom uređaju. Vaš promet preko sigurne mreže može se pratiti."</string>
- <string name="monitoring_description_management_network_logging" msgid="7184005419733060736">"Vaš administrator je uključio zapisivanje na mreži, čime se prati promet na vašem uređaju."</string>
+ <string name="monitoring_description_management_ca_certificate" msgid="5202023784131001751">"Vaša organizacija je instalirala CA certifikat na ovom uređaju. Vaš saobraćaj preko sigurne mreže može se pratiti."</string>
+ <string name="monitoring_description_managed_profile_ca_certificate" msgid="4683248196789897964">"Vaša organizacija je instalirala CA certifikat na vašem radnom profilu. Vaš saobraćaj preko sigurne mreže može se pratiti."</string>
+ <string name="monitoring_description_ca_certificate" msgid="7886985418413598352">"CA certifikat je instaliran na ovom uređaju. Vaš saobraćaj preko sigurne mreže može se pratiti."</string>
+ <string name="monitoring_description_management_network_logging" msgid="7184005419733060736">"Vaš administrator je uključio zapisivanje na mreži, čime se prati saobraćaj na vašem uređaju."</string>
<string name="monitoring_description_named_vpn" msgid="7403457334088909254">"Povezani ste s aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g> koja može pratiti vašu aktivnost na mreži, uključujući e-poštu i web lokacije."</string>
<string name="monitoring_description_two_named_vpns" msgid="4198511413729213802">"Povezani ste s aplikacijama <xliff:g id="VPN_APP_0">%1$s</xliff:g> i <xliff:g id="VPN_APP_1">%2$s</xliff:g> koje mogu pratiti vašu aktivnost na mreži, uključujući e-poštu, aplikacije i web lokacije."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="1427905889862420559">"Vaš radni profil je povezan s aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g>, koja može pratiti vašu aktivnost na mreži, uključujući e-poruke i web lokacije."</string>
@@ -462,7 +462,7 @@
<string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
<string name="monitoring_description_app" msgid="1828472472674709532">"Povezani ste s aplikacijom <xliff:g id="APPLICATION">%1$s</xliff:g>, koja može pratiti vašu aktivnost na mreži, uključujući e-poruke, aplikacije i web lokacije."</string>
<string name="monitoring_description_app_personal" msgid="484599052118316268">"Povezani ste sa aplikacijom <xliff:g id="APPLICATION">%1$s</xliff:g>, koja može pratiti vašu aktivnost na privatnoj mreži, uključujući e-mailove, aplikacije i web-lokacije."</string>
- <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Povezani ste na aplikaciju <xliff:g id="APPLICATION">%1$s</xliff:g>, koja može pratiti vaše privatne aktivnosti na mreži, uključujući e-poštu, aplikacije i web stranice."</string>
+ <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Povezani ste na aplikaciju <xliff:g id="APPLICATION">%1$s</xliff:g>, koja može pratiti vaše privatne aktivnosti na mreži, uključujući e-poštu, aplikacije i web lokacije."</string>
<string name="monitoring_description_app_work" msgid="4612997849787922906">"Vašim radnim profilom upravlja <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Profil je povezan s aplikacijom <xliff:g id="APPLICATION">%2$s</xliff:g>, koja može pratiti vašu aktivnost na radnoj mreži, uključujući e-poruke, aplikacije i web lokacije.\n\nZa više informacija, obratite se svom administratoru."</string>
<string name="monitoring_description_app_personal_work" msgid="5664165460056859391">"Radnim profilom upravlja <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Profil je povezan s aplikacijom <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, koja može pratiti vašu aktivnost na radnoj mreži, uključujući e-poruke, aplikacije i web lokacije.\n\nPovezani ste i sa aplikacijom <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, koja može pratiti vašu aktivnost na privatnoj mreži."</string>
<string name="keyguard_indication_trust_granted" msgid="4985003749105182372">"Otključano za korisnika <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
@@ -494,6 +494,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Dvostruka višemelodijska frekvencija"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Pristupačnost"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Pozivi"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"Zvono"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibriranje"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"Isključi zvuk"</string>
@@ -506,6 +507,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Dodirnite da isključite zvuk."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Prikazane kontrole jačine zvuka za: %s. Prevucite prema gore za odbacivanje."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Kontrole jačine zvuka sakrivene"</string>
+ <string name="output_title" msgid="5355078100792942802">"Izlaz za medijske fajlove"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Izlaz za telefonske pozive"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Nije pronađen nijedan uređaj"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Nije pronađen nijedan uređaj. Pokušajte uključiti <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth i Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Podešavač za korisnički interfejs sistema"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Prikaži ugrađeni postotak baterije"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Prikazuje postotak nivoa baterije unutar ikone na statusnoj traci kada se baterija ne puni"</string>
@@ -731,6 +739,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Proširi"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Umanji"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Zatvori"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Povucite prema dolje da odbacite"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Meni"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> je u načinu priakza Slika u slici"</string>
@@ -768,7 +778,7 @@
<string name="instant_apps_message" msgid="8116608994995104836">"Za instant aplikacije nije potrebna instalacija"</string>
<string name="app_info" msgid="6856026610594615344">"Informacije o aplikaciji"</string>
<string name="go_to_web" msgid="2650669128861626071">"Idi na preglednik"</string>
- <string name="mobile_data" msgid="7094582042819250762">"Mobilni podaci"</string>
+ <string name="mobile_data" msgid="7094582042819250762">"Prijenos podataka na mobilnoj mreži"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi veza je isključena"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth je isključen"</string>
<string name="dnd_is_off" msgid="6167780215212497572">"Opcija Ne ometaj je isključena"</string>
@@ -780,6 +790,6 @@
<string name="qs_dnd_replace" msgid="8019520786644276623">"Zamijeni"</string>
<string name="running_foreground_services_title" msgid="381024150898615683">"Aplikacije koje rade u pozadini"</string>
<string name="running_foreground_services_msg" msgid="6326247670075574355">"Dodirnite za detalje o potrošnji baterije i prijenosa podataka"</string>
- <string name="data_usage_disable_mobile" msgid="5116269981510015864">"Želite li isključiti prijenos mobilnih podataka?"</string>
+ <string name="data_usage_disable_mobile" msgid="5116269981510015864">"Želite li isključiti prijenos podataka na mobilnoj mreži?"</string>
<string name="touch_filtered_warning" msgid="8671693809204767551">"Postavke ne mogu potvrditi vaš odgovor jer aplikacija zaklanja zahtjev za odobrenje."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index dacf9d7..4ff8e2c 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -35,8 +35,8 @@
<string name="battery_low_title" msgid="6456385927409742437">"Queda poca bateria"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g>."</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g>. La funció Estalvi de bateria està activada."</string>
- <string name="invalid_charger" msgid="4549105996740522523">"Càrrega d\'USB no admesa.\nUtilitza només el carregador proporcionat."</string>
- <string name="invalid_charger_title" msgid="3515740382572798460">"La càrrega per USB no és compatible."</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"Pujada d\'USB no admesa.\nUtilitza només el carregador proporcionat."</string>
+ <string name="invalid_charger_title" msgid="3515740382572798460">"La pujada per USB no és compatible."</string>
<string name="invalid_charger_text" msgid="5474997287953892710">"Fes servir només el carregador proporcionat amb el dispositiu."</string>
<string name="battery_low_why" msgid="4553600287639198111">"Configuració"</string>
<string name="battery_saver_confirmation_title" msgid="2052100465684817154">"Vols activar la funció Estalvi de bateria?"</string>
@@ -342,7 +342,7 @@
<string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Divideix la pantalla cap a la dreta"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carregada"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"S\'està carregant"</string>
- <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> per completar la càrrega"</string>
+ <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> per completar la pujada"</string>
<string name="expanded_header_battery_not_charging" msgid="4798147152367049732">"No s\'està carregant"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"És possible que la xarxa\nestigui controlada"</string>
<string name="description_target_search" msgid="3091587249776033139">"Cerca"</string>
@@ -369,9 +369,9 @@
<string name="interruption_level_none_twoline" msgid="3957581548190765889">"Silenci\ntotal"</string>
<string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Només\ninterr. prior."</string>
<string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Només\nalarmes"</string>
- <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Carregant (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> per completar la càrrega)"</string>
- <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Càrrega ràpida (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> per completar-se)"</string>
- <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Càrrega lenta (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> per completar-se)"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Carregant (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> per completar la pujada)"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Pujada ràpida (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> per completar-se)"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Pujada lenta (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> per completar-se)"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Canvia d\'usuari"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Canvia d\'usuari. Usuari actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Usuari actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Toca per silenciar."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Es mostren %s controls de volum. Llisca cap amunt per ignorar-ho."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Els controls de volum estan amagats"</string>
+ <string name="output_title" msgid="5355078100792942802">"Sortida de contingut multimèdia"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Sortida de trucades"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"No s\'ha trobat cap dispositiu"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"No s\'ha trobat cap dispositiu. Prova d\'activar <xliff:g id="SERVICE">%1$s</xliff:g>."</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth i Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Personalitzador d\'interfície d\'usuari"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Mostra el percentatge de la bateria inserit"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Mostra el percentatge del nivell de bateria dins de la icona de la barra d\'estat quan no s\'estigui carregant"</string>
@@ -584,7 +591,7 @@
<item quantity="one">%d minut</item>
</plurals>
<string name="battery_panel_title" msgid="7944156115535366613">"Consum de la bateria"</string>
- <string name="battery_detail_charging_summary" msgid="1279095653533044008">"La funció Estalvi de bateria no està disponible durant la càrrega"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"La funció Estalvi de bateria no està disponible durant la pujada"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Estalvi de bateria"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Redueix el rendiment i les dades en segon pla"</string>
<string name="keyboard_key_button_template" msgid="6230056639734377300">"Botó <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -724,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Desplega"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimitza"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Tanca"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Arrossega cap avall per ignorar-ho"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menú"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> està en pantalla en pantalla"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 4166697..bb0d362 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -35,7 +35,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Probíhající"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Oznámení"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Baterie je slabá"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Baterie je téměř vybitá, zapněte spořič baterie"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"Zbývá <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Zbývá <xliff:g id="PERCENTAGE">%s</xliff:g>, při obvyklém využití asi <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Zbývá <xliff:g id="PERCENTAGE">%s</xliff:g>, asi <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Zbývá <xliff:g id="PERCENTAGE">%s</xliff:g>. Spořič baterie je zapnutý."</string>
<string name="invalid_charger" msgid="4549105996740522523">"Nabíjení pomocí rozhraní USB není podporováno.\nPoužívejte pouze nabíječku, která byla dodána se zařízením."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"Nabíjení přes USB není podporováno."</string>
@@ -69,14 +72,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Uživatel aktuálně přihlášený k tomuto zařízení nemůže zapnout ladění USB. Chcete-li tuto funkci použít, přepněte na primárního uživatele."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Přiblížit na celou obrazovku"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Na celou obrazovku"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Snímek obrazovky"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Ukládání snímku obrazovky..."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Ukládání snímku obrazovky..."</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Probíhá ukládání snímku obrazovky."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Snímek obrazovky pořízen"</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Klepnutím zobrazíte snímek obrazovky."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Snímek obrazovky se nepodařilo zachytit."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Při ukládání snímku obrazovky došlo k problému."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Snímek obrazovky nelze pořídit kvůli nedostatku místa v úložišti."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"Ukládání snímku obrazovky"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"Snímek obrazovky byl uložen"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Klepnutím snímek obrazovky zobrazíte"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"Snímek obrazovky se nepodařilo zachytit"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Při ukládání snímku obrazovky došlo k problému"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Snímek obrazovky kvůli nedostatku místa v úložišti nelze uložit"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Aplikace nebo organizace zakazuje pořizování snímků obrazovky"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Možnosti přenosu souborů pomocí rozhraní USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Připojit jako přehrávač médií (MTP)"</string>
@@ -509,6 +513,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Klepnutím vypnete zvuk."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Ovládací prvky hlasitosti aplikace %s jsou zobrazeny. Zavřete je přejetím prstem."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Ovládací prvky hlasitosti jsou skryty"</string>
+ <string name="output_title" msgid="5355078100792942802">"Výstup médií"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Výstup telefonního hovoru"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Nebyla nalezena žádná zařízení"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Nebyla nalezena žádná zařízení. Zkuste zapnout <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth a Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Nástroj na ladění uživatelského rozhraní systému"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Zobrazovat vložené procento nabití baterie"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Když neprobíhá nabíjení, zobrazit v ikoně na stavovém řádku procento nabití baterie"</string>
@@ -556,30 +567,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Vypnuto"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"Rozšířené ovládací prvky oznámení umožňují nastavit úroveň důležitosti oznámení aplikace od 0 do 5. \n\n"<b>"Úroveň 5"</b>" \n– Zobrazit na začátku seznamu oznámení \n– Povolit vyrušení na celou obrazovku \n– Vždy zobrazit náhled \n\n"<b>"Úroveň 4"</b>" \n– Zabránit vyrušení na celou obrazovku \n– Vždy zobrazit náhled \n\n"<b>"Úroveň 3"</b>" \n– Zabránit vyrušení na celou obrazovku \n– Nikdy nezobrazovat náhled \n\n"<b>"Úroveň 2"</b>" \n– Zabránit vyrušení na celou obrazovku \n– Nikdy nezobrazovat náhled \n– Nikdy nevydávat žádný zvukový signál ani nevibrovat \n\n"<b>"Úroveň 1"</b>" \n– Zabránit vyrušení na celou obrazovku \n– Nikdy nezobrazovat náhled \n– Nikdy nevydávat zvukový signál ani nevibrovat \n– Skrýt z obrazovky uzamčení a stavového řádku \n– Zobrazovat na konci seznamu oznámení \n\n"<b>";Úroveň 0"</b>" \n– Blokovat všechna oznámení z aplikace"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Oznámení"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"Tato oznámení již nebudete dostávat"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"Kategorie oznámení: <xliff:g id="NUMBER">%d</xliff:g>"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"Tato aplikace nemá kategorie oznámení"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Oznámení této aplikace nelze vypnout"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="few">1 ze <xliff:g id="NUMBER_1">%s</xliff:g> kategorií oznámení z této aplikace</item>
- <item quantity="many">1 z <xliff:g id="NUMBER_1">%s</xliff:g> kategorie oznámení z této aplikace</item>
- <item quantity="other">1 z <xliff:g id="NUMBER_1">%s</xliff:g> kategorií oznámení z této aplikace</item>
- <item quantity="one">1 z <xliff:g id="NUMBER_0">%s</xliff:g> kategorie oznámení z této aplikace</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="few"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> a <xliff:g id="NUMBER_5">%3$d</xliff:g> další</item>
- <item quantity="many"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> a <xliff:g id="NUMBER_5">%3$d</xliff:g> dalšího</item>
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> a <xliff:g id="NUMBER_5">%3$d</xliff:g> dalších</item>
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> a <xliff:g id="NUMBER_2">%3$d</xliff:g> další</item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"Tato oznámení již nebudete dostávat"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"Mají se tato oznámení nadále zobrazovat?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"Přestat zobrazovat oznámení"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Nadále zobrazovat"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"Mají se oznámení z této aplikace nadále zobrazovat?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"Tato oznámení nelze deaktivovat"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Ovládací prvky oznámení aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> byly otevřeny"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Ovládací prvky oznámení aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> byly zavřeny"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Povolit oznámení z tohoto kanálu"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"Všechny kategorie"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Další nastavení"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Přizpůsobit: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"Upravit"</string>
<string name="notification_done" msgid="5279426047273930175">"Hotovo"</string>
+ <string name="inline_undo" msgid="558916737624706010">"Zpět"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g> aplikace <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"Nastavení oznámení"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"Možnosti odložení oznámení"</string>
@@ -738,6 +738,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Rozbalit"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimalizovat"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Zavřít"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Nastavení"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Nápovědu zavřete přetažením dolů"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Nabídka"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"Aplikace <xliff:g id="NAME">%s</xliff:g> je v režimu obraz v obraze"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 52446f8..5438434 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Tryk for at slå lyden fra."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Lydstyrkeknapperne for %s er synlige. Stryg op for at lukke."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Lydstyrkeknapperne er skjult"</string>
+ <string name="output_title" msgid="5355078100792942802">"Medieafspilning"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Udgang til telefonopkald"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Der blev ikke fundet nogen enheder"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Der blev ikke fundet nogen enheder. Prøv at aktivere <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth og Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Vis procent for det indbyggede batteri"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Vis procenttallet for batteriniveauet i ikonet for statusbjælken, når der ikke oplades"</string>
@@ -724,6 +731,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Udvid"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimer"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Luk"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Indstillinger"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Træk nedad for at afvise"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> vises som integreret billede"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 83a8e33..77a666d 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -33,7 +33,13 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Aktuell"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Benachrichtigungen"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Akku ist schwach"</string>
+ <!-- no translation found for battery_low_title_hybrid (6268991275887381595) -->
+ <skip />
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> verbleibend"</string>
+ <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) -->
+ <skip />
+ <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) -->
+ <skip />
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Noch <xliff:g id="PERCENTAGE">%s</xliff:g>. Der Energiesparmodus ist aktiviert."</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB-Aufladung wird nicht unterstützt.\nVerwende das mitgelieferte Aufladegerät."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"Laden per USB wird nicht unterstützt."</string>
@@ -67,14 +73,22 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Der momentan auf diesem Gerät angemeldete Nutzer kann das USB-Debugging nicht aktivieren. Um diese Funktion verwenden zu können, wechsle zum primären Nutzer."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom auf Bildschirmgröße"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Auf Bildschirmgröße anpassen"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Screenshot wird gespeichert..."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Screenshot wird gespeichert..."</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Screenshot wird gespeichert..."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot aufgenommen"</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Tippe, um deinen Screenshot anzusehen."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Screenshot konnte nicht aufgenommen werden."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Beim Speichern des Screenshots ist ein Problem aufgetreten."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Speichern des Screenshots aufgrund von zu wenig Speicher nicht möglich."</string>
+ <!-- no translation found for screenshot_saving_text (2545047868936087248) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_title (5637073968117370753) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_text (7574667448002050363) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_title (9096484883063264803) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) -->
+ <skip />
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Die App oder deine Organisation lässt das Erstellen von Screenshots nicht zu"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB-Dateiübertragungsoptionen"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Als Medienplayer (MTP) bereitstellen"</string>
@@ -507,6 +521,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Zum Stummschalten tippen."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Lautstärkeregler von %s werden angezeigt. Zum Schließen nach oben wischen."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Lautstärkeregler ausgeblendet"</string>
+ <string name="output_title" msgid="5355078100792942802">"Medienausgabe"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Telefonanrufausgabe"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Keine Geräte gefunden"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Keine Geräte gefunden. Aktiviere <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"WLAN"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth und WLAN"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Eingebettete Akku-Prozentzahl anzeigen"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Prozentzahl für Akkustand in Statusleistensymbol anzeigen, wenn das Gerät nicht geladen wird"</string>
@@ -554,26 +575,27 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Aus"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"Mit den erweiterten Benachrichtigungseinstellungen kannst du für App-Benachrichtigungen eine Wichtigkeitsstufe von 0 bis 5 festlegen. \n\n"<b>"Stufe 5"</b>" \n- Auf der Benachrichtigungsleiste ganz oben anzeigen \n- Vollbildunterbrechung zulassen \n- Immer kurz einblenden \n\n"<b>"Stufe 4"</b>" \n- Keine Vollbildunterbrechung \n- Immer kurz einblenden \n\n"<b>"Stufe 3"</b>" \n- Keine Vollbildunterbrechung \n- Nie kurz einblenden \n\n"<b>"Stufe 2"</b>" \n- Keine Vollbildunterbrechung \n- Nie kurz einblenden \n- Weder Ton noch Vibration \n\n"<b>"Stufe 1"</b>" \n- Keine Vollbildunterbrechung \n- Nie kurz einblenden \n- Weder Ton noch Vibration \n- Auf Sperrbildschirm und Statusleiste verbergen \n- Auf der Benachrichtigungsleiste ganz unten anzeigen \n\n"<b>"Stufe 0"</b>" \n- Alle Benachrichtigungen der App sperren"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Benachrichtigungen"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"Du erhältst diese Benachrichtigungen nicht mehr"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> Benachrichtigungskategorien"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"Diese App hat keine Benachrichtigungskategorien"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Benachrichtigungen von dieser App können nicht deaktiviert werden"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="other">1 von <xliff:g id="NUMBER_1">%s</xliff:g> Benachrichtigungskategorien von dieser App</item>
- <item quantity="one">1 von <xliff:g id="NUMBER_0">%s</xliff:g> Benachrichtigungskategorie von dieser App</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> und <xliff:g id="NUMBER_5">%3$d</xliff:g> andere</item>
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> und <xliff:g id="NUMBER_2">%3$d</xliff:g> andere</item>
- </plurals>
+ <!-- no translation found for notification_channel_disabled (344536703863700565) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing (8945102997083836858) -->
+ <skip />
+ <!-- no translation found for inline_stop_button (4172980096860941033) -->
+ <skip />
+ <!-- no translation found for inline_keep_button (6665940297019018232) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing_app (1723113469580031041) -->
+ <skip />
+ <!-- no translation found for notification_unblockable_desc (1037434112919403708) -->
+ <skip />
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Benachrichtigungseinstellungen für <xliff:g id="APP_NAME">%1$s</xliff:g> geöffnet"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Benachrichtigungseinstellungen für <xliff:g id="APP_NAME">%1$s</xliff:g> geschlossen"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Benachrichtigungen von diesem Kanal zulassen"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"Alle Kategorien"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Weitere Einstellungen"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Anpassen: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <!-- no translation found for notification_app_settings (420348114670768449) -->
+ <skip />
<string name="notification_done" msgid="5279426047273930175">"Fertig"</string>
+ <!-- no translation found for inline_undo (558916737624706010) -->
+ <skip />
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g> – <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"Benachrichtigungseinstellungen"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"Optionen für spätere Erinnerung bei Benachrichtigungen"</string>
@@ -728,6 +750,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Maximieren"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimieren"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Schließen"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Einstellungen"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Zum Schließen nach unten ziehen"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menü"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> ist in Bild im Bild"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 9a61828..7464d31 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Πατήστε για σίγαση."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Εμφανίζονται τα στοιχεία ελέγχου έντασης %s. Σύρετε για παράβλεψη."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Έγινε απόκρυψη των στοιχείων ελέγχου έντασης"</string>
+ <string name="output_title" msgid="5355078100792942802">"Έξοδος μέσων"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Έξοδος τηλεφωνικής κλήσης"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Δεν βρέθηκαν συσκευές"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Δεν βρέθηκαν συσκευές. Δοκιμάστε να ενεργοποιήσετε την υπηρεσία <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth και Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Εμφάνιση ποσοστού ενσωματωμένης μπαταρίας"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Εμφάνιση ποσοστού επιπέδου μπαταρίας μέσα στο εικονίδιο της γραμμής κατάστασης όταν δεν γίνεται φόρτιση"</string>
@@ -724,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Ανάπτυξη"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Ελαχιστοποίηση"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Κλείσιμο"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Σύρετε προς τα κάτω για παράβλεψη"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Μενού"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"Η λειτουργία picture-in-picture είναι ενεργή σε <xliff:g id="NAME">%s</xliff:g>."</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 87a0ee5..ab759ed 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -33,7 +33,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ongoing"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Battery is low"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Battery is low. Turn on Battery Saver"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left based on your usage"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining. Battery Saver is on."</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB charging not supported.\nUse only the supplied charger."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"USB charging not supported."</string>
@@ -67,14 +70,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to the primary user."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom to fill screen"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Stretch to fill screen"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Saving screenshot…"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Saving screenshot…"</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Screenshot is being saved."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot captured."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Tap to view your screenshot."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Couldn\'t capture screenshot."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problem encountered while saving screenshot."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Can\'t save screenshot due to limited storage space."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"Screenshot is being saved"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"Screenshot saved"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Tap to view your screenshot"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"Couldn\'t capture screenshot"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Problem encountered while saving screenshot"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Can\'t save screenshot due to limited storage space"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Taking screenshots isn\'t allowed by the app or your organisation"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB file transfer options"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Mount as a media player (MTP)"</string>
@@ -492,6 +496,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Dual multi-tone frequency"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Accessibility"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Calls"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Ring"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrate"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Mute"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Tap to unmute."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Tap to mute. Accessibility services may be muted."</string>
@@ -499,6 +507,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Tap to mute."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s volume controls shown. Swipe up to dismiss."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Volume controls hidden"</string>
+ <string name="output_title" msgid="5355078100792942802">"Media output"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Phone call output"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"No devices found"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"No devices found. Try turning on <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth and Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Show embedded battery percentage"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Show battery level percentage inside the status bar icon when not charging"</string>
@@ -546,26 +561,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Off"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications. \n\n"<b>"Level 5"</b>" \n- Show at the top of the notification list \n- Allow full screen interruption \n- Always peek \n\n"<b>"Level 4"</b>" \n- Prevent full screen interruption \n- Always peek \n\n"<b>"Level 3"</b>" \n- Prevent full screen interruption \n- Never peek \n\n"<b>"Level 2"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound and vibration \n\n"<b>"Level 1"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound or vibrate \n- Hide from lock screen and status bar \n- Show at the bottom of the notification list \n\n"<b>"Level 0"</b>" \n- Block all notifications from the app"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"You won\'t get these notifications anymore"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> notification categories"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"This app doesn\'t have notification categories"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Notifications from this app can\'t be turned off"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="other">1 out of <xliff:g id="NUMBER_1">%s</xliff:g> notification categories from this app</item>
- <item quantity="one">1 out of <xliff:g id="NUMBER_0">%s</xliff:g> notification category from this app</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, and <xliff:g id="NUMBER_5">%3$d</xliff:g> others</item>
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>, and <xliff:g id="NUMBER_2">%3$d</xliff:g> other</item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"You won\'t see these notifications anymore"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"Keep showing these notifications?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"Stop notifications"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Keep showing"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"These notifications can\'t be turned off"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> closed"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Allow notifications from this channel"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"All Categories"</string>
<string name="notification_more_settings" msgid="816306283396553571">"More settings"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Customise: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"Customise"</string>
<string name="notification_done" msgid="5279426047273930175">"Done"</string>
+ <string name="inline_undo" msgid="558916737624706010">"Undo"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"notification controls"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"notification snooze options"</string>
@@ -720,6 +728,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Expand"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimise"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Close"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Settings"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Drag down to dismiss"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
@@ -756,7 +765,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Instant apps don\'t require installation."</string>
<string name="app_info" msgid="6856026610594615344">"App info"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Go to web"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Go to browser"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobile data"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi is off"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth is off"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 6756f86..1bd101f 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -33,7 +33,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ongoing"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Battery is low"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Battery is low. Turn on Battery Saver"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left based on your usage"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining. Battery Saver is on."</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB charging not supported.\nUse only the supplied charger."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"USB charging not supported."</string>
@@ -67,14 +70,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to the primary user."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom to fill screen"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Stretch to fill screen"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Saving screenshot…"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Saving screenshot…"</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Screenshot is being saved."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot captured."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Tap to view your screenshot."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Couldn\'t capture screenshot."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problem encountered while saving screenshot."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Can\'t save screenshot due to limited storage space."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"Screenshot is being saved"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"Screenshot saved"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Tap to view your screenshot"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"Couldn\'t capture screenshot"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Problem encountered while saving screenshot"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Can\'t save screenshot due to limited storage space"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Taking screenshots isn\'t allowed by the app or your organisation"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB file transfer options"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Mount as a media player (MTP)"</string>
@@ -492,6 +496,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Dual multi-tone frequency"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Accessibility"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Calls"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Ring"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrate"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Mute"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Tap to unmute."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Tap to mute. Accessibility services may be muted."</string>
@@ -499,6 +507,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Tap to mute."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s volume controls shown. Swipe up to dismiss."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Volume controls hidden"</string>
+ <string name="output_title" msgid="5355078100792942802">"Media output"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Phone call output"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"No devices found"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"No devices found. Try turning on <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth and Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Show embedded battery percentage"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Show battery level percentage inside the status bar icon when not charging"</string>
@@ -546,26 +561,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Off"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications. \n\n"<b>"Level 5"</b>" \n- Show at the top of the notification list \n- Allow full screen interruption \n- Always peek \n\n"<b>"Level 4"</b>" \n- Prevent full screen interruption \n- Always peek \n\n"<b>"Level 3"</b>" \n- Prevent full screen interruption \n- Never peek \n\n"<b>"Level 2"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound and vibration \n\n"<b>"Level 1"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound or vibrate \n- Hide from lock screen and status bar \n- Show at the bottom of the notification list \n\n"<b>"Level 0"</b>" \n- Block all notifications from the app"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"You won\'t get these notifications anymore"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> notification categories"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"This app doesn\'t have notification categories"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Notifications from this app can\'t be turned off"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="other">1 out of <xliff:g id="NUMBER_1">%s</xliff:g> notification categories from this app</item>
- <item quantity="one">1 out of <xliff:g id="NUMBER_0">%s</xliff:g> notification category from this app</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, and <xliff:g id="NUMBER_5">%3$d</xliff:g> others</item>
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>, and <xliff:g id="NUMBER_2">%3$d</xliff:g> other</item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"You won\'t see these notifications anymore"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"Keep showing these notifications?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"Stop notifications"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Keep showing"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"These notifications can\'t be turned off"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> closed"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Allow notifications from this channel"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"All Categories"</string>
<string name="notification_more_settings" msgid="816306283396553571">"More settings"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Customise: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"Customise"</string>
<string name="notification_done" msgid="5279426047273930175">"Done"</string>
+ <string name="inline_undo" msgid="558916737624706010">"Undo"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"notification controls"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"notification snooze options"</string>
@@ -720,6 +728,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Expand"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimise"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Close"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Settings"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Drag down to dismiss"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
@@ -756,7 +765,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Instant apps don\'t require installation."</string>
<string name="app_info" msgid="6856026610594615344">"App info"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Go to web"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Go to browser"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobile data"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi is off"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth is off"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 87a0ee5..ab759ed 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -33,7 +33,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ongoing"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Battery is low"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Battery is low. Turn on Battery Saver"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left based on your usage"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining. Battery Saver is on."</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB charging not supported.\nUse only the supplied charger."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"USB charging not supported."</string>
@@ -67,14 +70,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to the primary user."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom to fill screen"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Stretch to fill screen"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Saving screenshot…"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Saving screenshot…"</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Screenshot is being saved."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot captured."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Tap to view your screenshot."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Couldn\'t capture screenshot."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problem encountered while saving screenshot."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Can\'t save screenshot due to limited storage space."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"Screenshot is being saved"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"Screenshot saved"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Tap to view your screenshot"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"Couldn\'t capture screenshot"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Problem encountered while saving screenshot"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Can\'t save screenshot due to limited storage space"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Taking screenshots isn\'t allowed by the app or your organisation"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB file transfer options"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Mount as a media player (MTP)"</string>
@@ -492,6 +496,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Dual multi-tone frequency"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Accessibility"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Calls"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Ring"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrate"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Mute"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Tap to unmute."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Tap to mute. Accessibility services may be muted."</string>
@@ -499,6 +507,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Tap to mute."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s volume controls shown. Swipe up to dismiss."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Volume controls hidden"</string>
+ <string name="output_title" msgid="5355078100792942802">"Media output"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Phone call output"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"No devices found"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"No devices found. Try turning on <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth and Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Show embedded battery percentage"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Show battery level percentage inside the status bar icon when not charging"</string>
@@ -546,26 +561,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Off"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications. \n\n"<b>"Level 5"</b>" \n- Show at the top of the notification list \n- Allow full screen interruption \n- Always peek \n\n"<b>"Level 4"</b>" \n- Prevent full screen interruption \n- Always peek \n\n"<b>"Level 3"</b>" \n- Prevent full screen interruption \n- Never peek \n\n"<b>"Level 2"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound and vibration \n\n"<b>"Level 1"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound or vibrate \n- Hide from lock screen and status bar \n- Show at the bottom of the notification list \n\n"<b>"Level 0"</b>" \n- Block all notifications from the app"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"You won\'t get these notifications anymore"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> notification categories"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"This app doesn\'t have notification categories"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Notifications from this app can\'t be turned off"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="other">1 out of <xliff:g id="NUMBER_1">%s</xliff:g> notification categories from this app</item>
- <item quantity="one">1 out of <xliff:g id="NUMBER_0">%s</xliff:g> notification category from this app</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, and <xliff:g id="NUMBER_5">%3$d</xliff:g> others</item>
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>, and <xliff:g id="NUMBER_2">%3$d</xliff:g> other</item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"You won\'t see these notifications anymore"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"Keep showing these notifications?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"Stop notifications"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Keep showing"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"These notifications can\'t be turned off"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> closed"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Allow notifications from this channel"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"All Categories"</string>
<string name="notification_more_settings" msgid="816306283396553571">"More settings"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Customise: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"Customise"</string>
<string name="notification_done" msgid="5279426047273930175">"Done"</string>
+ <string name="inline_undo" msgid="558916737624706010">"Undo"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"notification controls"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"notification snooze options"</string>
@@ -720,6 +728,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Expand"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimise"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Close"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Settings"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Drag down to dismiss"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
@@ -756,7 +765,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Instant apps don\'t require installation."</string>
<string name="app_info" msgid="6856026610594615344">"App info"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Go to web"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Go to browser"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobile data"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi is off"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth is off"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 87a0ee5..ab759ed 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -33,7 +33,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ongoing"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Battery is low"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Battery is low. Turn on Battery Saver"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left based on your usage"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining. Battery Saver is on."</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB charging not supported.\nUse only the supplied charger."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"USB charging not supported."</string>
@@ -67,14 +70,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to the primary user."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom to fill screen"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Stretch to fill screen"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Saving screenshot…"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Saving screenshot…"</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Screenshot is being saved."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot captured."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Tap to view your screenshot."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Couldn\'t capture screenshot."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problem encountered while saving screenshot."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Can\'t save screenshot due to limited storage space."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"Screenshot is being saved"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"Screenshot saved"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Tap to view your screenshot"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"Couldn\'t capture screenshot"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Problem encountered while saving screenshot"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Can\'t save screenshot due to limited storage space"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Taking screenshots isn\'t allowed by the app or your organisation"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB file transfer options"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Mount as a media player (MTP)"</string>
@@ -492,6 +496,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Dual multi-tone frequency"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Accessibility"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Calls"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Ring"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrate"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Mute"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Tap to unmute."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Tap to mute. Accessibility services may be muted."</string>
@@ -499,6 +507,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Tap to mute."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s volume controls shown. Swipe up to dismiss."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Volume controls hidden"</string>
+ <string name="output_title" msgid="5355078100792942802">"Media output"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Phone call output"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"No devices found"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"No devices found. Try turning on <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth and Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Show embedded battery percentage"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Show battery level percentage inside the status bar icon when not charging"</string>
@@ -546,26 +561,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Off"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications. \n\n"<b>"Level 5"</b>" \n- Show at the top of the notification list \n- Allow full screen interruption \n- Always peek \n\n"<b>"Level 4"</b>" \n- Prevent full screen interruption \n- Always peek \n\n"<b>"Level 3"</b>" \n- Prevent full screen interruption \n- Never peek \n\n"<b>"Level 2"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound and vibration \n\n"<b>"Level 1"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound or vibrate \n- Hide from lock screen and status bar \n- Show at the bottom of the notification list \n\n"<b>"Level 0"</b>" \n- Block all notifications from the app"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"You won\'t get these notifications anymore"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> notification categories"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"This app doesn\'t have notification categories"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Notifications from this app can\'t be turned off"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="other">1 out of <xliff:g id="NUMBER_1">%s</xliff:g> notification categories from this app</item>
- <item quantity="one">1 out of <xliff:g id="NUMBER_0">%s</xliff:g> notification category from this app</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, and <xliff:g id="NUMBER_5">%3$d</xliff:g> others</item>
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>, and <xliff:g id="NUMBER_2">%3$d</xliff:g> other</item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"You won\'t see these notifications anymore"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"Keep showing these notifications?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"Stop notifications"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Keep showing"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"These notifications can\'t be turned off"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> closed"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Allow notifications from this channel"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"All Categories"</string>
<string name="notification_more_settings" msgid="816306283396553571">"More settings"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Customise: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"Customise"</string>
<string name="notification_done" msgid="5279426047273930175">"Done"</string>
+ <string name="inline_undo" msgid="558916737624706010">"Undo"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"notification controls"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"notification snooze options"</string>
@@ -720,6 +728,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Expand"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimise"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Close"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Settings"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Drag down to dismiss"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
@@ -756,7 +765,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Instant apps don\'t require installation."</string>
<string name="app_info" msgid="6856026610594615344">"App info"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Go to web"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Go to browser"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobile data"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi is off"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth is off"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 1d70422..ad39146 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -33,7 +33,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ongoing"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Battery is low"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Battery is low. Turn on Battery Saver"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left based on your usage"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining. Battery Saver is on."</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB charging not supported.\nUse only the supplied charger."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"USB charging not supported."</string>
@@ -67,14 +70,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to the primary user."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom to fill screen"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Stretch to fill screen"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Saving screenshot…"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Saving screenshot…"</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Screenshot is being saved."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot captured."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Tap to view your screenshot."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Couldn\'t capture screenshot."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problem encountered while saving screenshot."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Can\'t save screenshot due to limited storage space."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"Screenshot is being saved"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"Screenshot saved"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Tap to view your screenshot"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"Couldn\'t capture screenshot"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Problem encountered while saving screenshot"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Can\'t save screenshot due to limited storage space"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Taking screenshots isn\'t allowed by the app or your organization"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB file transfer options"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Mount as a media player (MTP)"</string>
@@ -492,6 +496,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Dual multi tone frequency"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Accessibility"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Calls"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Ring"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrate"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Mute"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Tap to unmute."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Tap to mute. Accessibility services may be muted."</string>
@@ -499,6 +507,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Tap to mute."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s volume controls shown. Swipe up to dismiss."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Volume controls hidden"</string>
+ <string name="output_title" msgid="5355078100792942802">"Media output"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Phone call output"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"No devices found"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"No devices found. Try turning on <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth and Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Show embedded battery percentage"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Show battery level percentage inside the status bar icon when not charging"</string>
@@ -546,26 +561,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Off"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications. \n\n"<b>"Level 5"</b>" \n- Show at the top of the notification list \n- Allow full screen interruption \n- Always peek \n\n"<b>"Level 4"</b>" \n- Prevent full screen interruption \n- Always peek \n\n"<b>"Level 3"</b>" \n- Prevent full screen interruption \n- Never peek \n\n"<b>"Level 2"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound and vibration \n\n"<b>"Level 1"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound or vibrate \n- Hide from lock screen and status bar \n- Show at the bottom of the notification list \n\n"<b>"Level 0"</b>" \n- Block all notifications from the app"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"You won\'t get these notifications anymore"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> notification categories"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"This app doesn\'t have notification categories"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Notifications from this app can\'t be turned off"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="other">1 out of <xliff:g id="NUMBER_1">%s</xliff:g> notification categories from this app</item>
- <item quantity="one">1 out of <xliff:g id="NUMBER_0">%s</xliff:g> notification category from this app</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, and <xliff:g id="NUMBER_5">%3$d</xliff:g> others</item>
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>, and <xliff:g id="NUMBER_2">%3$d</xliff:g> other</item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"You won\'t see these notifications anymore"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"Keep showing these notifications?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"Stop notifications"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Keep showing"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"These notifications can\'t be turned off"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> closed"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Allow notifications from this channel"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"All Categories"</string>
<string name="notification_more_settings" msgid="816306283396553571">"More settings"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Customize: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"Customize"</string>
<string name="notification_done" msgid="5279426047273930175">"Done"</string>
+ <string name="inline_undo" msgid="558916737624706010">"Undo"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"notification controls"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"notification snooze options"</string>
@@ -720,6 +728,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Expand"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimize"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Close"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Settings"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Drag down to dismiss"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
@@ -756,7 +765,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Instant apps don\'t require installation."</string>
<string name="app_info" msgid="6856026610594615344">"App info"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Go to web"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Go to browser"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobile data"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi is off"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth is off"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index d808119..6901686 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -505,6 +505,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Presiona para silenciar."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Se muestran los controles de volumen de %s. Desliza el dedo para descartar."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Controles de volumen ocultos"</string>
+ <string name="output_title" msgid="5355078100792942802">"Salida multimedia"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Salida de llamada telefónica"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"No se encontraron dispositivos"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"No se encontraron dispositivos. Activa <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth y Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Sintonizador de IU del sistema"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Mostrar porcentaje de la batería integrada"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Mostrar porcentaje del nivel de batería en el ícono de la barra de estado cuando no se está cargando"</string>
@@ -726,6 +733,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Expandir"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimizar"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Cerrar"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Arrastra hacia abajo para descartar"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menú"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> está en modo de imagen en imagen"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 14c2818..a5d5c44 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -494,6 +494,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Multifrecuencia de tono dual"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Accesibilidad"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Llamadas"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"Hacer sonar"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrar"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"Silenciar"</string>
@@ -504,6 +505,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Toca para silenciar."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s controles de volumen mostrados. Desliza el dedo hacia arriba para rechazar."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Controles de volumen ocultos"</string>
+ <string name="output_title" msgid="5355078100792942802">"Salida multimedia"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Salida de llamadas"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"No se ha podido encontrar ningún dispositivo"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"No se ha podido encontrar ningún dispositivo. Prueba a activar <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi‑Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth y Wi‑Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Configurador de IU del sistema"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Mostrar porcentaje de batería insertado"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Mostrar el porcentaje del nivel de batería en el icono de la barra de estado cuando no se esté cargando"</string>
@@ -725,6 +733,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Mostrar"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimizar"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Cerrar"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Ajustes"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Arrastra hacia abajo para ignorar"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menú"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> está en imagen en imagen"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 3a5af25..84cbc44 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -505,6 +505,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Puudutage vaigistamiseks."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s helitugevuse juhtnuppu on kuvatud. Loobumiseks pühkige üles."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Helitugevuse juhtnupud on peidetud"</string>
+ <string name="output_title" msgid="5355078100792942802">"Meediaväljund"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Telefonikõne väljund"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Seadmeid ei leitud"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Seadmeid ei leitud. Lülitage sisse teenus <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"WiFi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth ja WiFi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Süsteemi kasutajaliidese tuuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Kuva lisatud akutaseme protsent"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Akutaseme protsendi kuvamine olekuriba ikoonil, kui akut ei laeta"</string>
@@ -726,6 +733,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Laiendamine"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimeeri"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Sule"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Loobumiseks lohistage alla"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menüü"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> on režiimis Pilt pildis"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 87348de..1e5274e 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -33,7 +33,13 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Abian"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Jakinarazpenak"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Bateria agortzen ari da"</string>
+ <!-- no translation found for battery_low_title_hybrid (6268991275887381595) -->
+ <skip />
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> gelditzen da"</string>
+ <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) -->
+ <skip />
+ <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) -->
+ <skip />
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> gelditzen da. Bateria-aurrezlea aktibatuta dago."</string>
<string name="invalid_charger" msgid="4549105996740522523">"Ez da USB bidez kargatzea onartzen.\nErabili hornitu zaizun kargagailua soilik."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"Ez da USB bidez kargatzea onartzen."</string>
@@ -67,14 +73,22 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Gailu honetan saioa hasita duen erabiltzaileak ezin du aktibatu USB arazketa. Eginbide hori erabiltzeko, aldatu erabiltzaile nagusira."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Handiagotu pantaila betetzeko"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Luzatu pantaila betetzeko"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Pantaila-argazkia gordetzen…"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Pantaila-argazkia gordetzen…"</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Pantaila-argazkia gordetzen ari da."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Pantaila-argazkia atera da."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Sakatu pantaila-argazkia ikusteko."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Ezin izan da pantaila-argazkia atera."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Arazo bat izan da pantaila-argazkia gordetzean."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Ezin da atera pantaila-argazkia ez delako tokirik geratzen."</string>
+ <!-- no translation found for screenshot_saving_text (2545047868936087248) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_title (5637073968117370753) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_text (7574667448002050363) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_title (9096484883063264803) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) -->
+ <skip />
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Aplikazioak edo erakundeak ez du onartzen pantaila-argazkiak ateratzea"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB fitxategiak transferitzeko aukerak"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Muntatu multimedia-erreproduzigailu gisa (MTP)"</string>
@@ -226,8 +240,8 @@
<string name="accessibility_quick_settings_flashlight_on" msgid="2003479320007841077">"Flasha aktibatuta dago."</string>
<string name="accessibility_quick_settings_flashlight_changed_off" msgid="3303701786768224304">"Flasha desaktibatu egin da."</string>
<string name="accessibility_quick_settings_flashlight_changed_on" msgid="6531793301533894686">"Flasha aktibatu egin da."</string>
- <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="4406577213290173911">"Koloreak alderantzikatzeko aukera desaktibatu egin da."</string>
- <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="6897462320184911126">"Koloreak alderantzikatzeko aukera aktibatu egin da."</string>
+ <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="4406577213290173911">"Koloreen alderantzikatzea desaktibatu egin da."</string>
+ <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="6897462320184911126">"Koloreen alderantzikatzea aktibatu egin da."</string>
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="5004708003447561394">"Konexioa partekatzeko aukera desaktibatu egin da."</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2890951609226476206">"Konexioa partekatzeko aukera aktibatu egin da."</string>
<string name="accessibility_casting_turned_off" msgid="1430668982271976172">"Pantaila igortzeari utzi zaio."</string>
@@ -505,6 +519,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Sakatu hau audioa desaktibatzeko."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Bolumena kontrolatzeko %s aukera daude ikusgai. Pasatu hatza gora baztertzeko."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Ezkutatuta daude bolumena kontrolatzeko aukerak"</string>
+ <string name="output_title" msgid="5355078100792942802">"Multimedia-irteera"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Telefono-deiaren irteera"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Ez da aurkitu gailurik"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Ez da aurkitu gailurik. Aktibatu <xliff:g id="SERVICE">%1$s</xliff:g>."</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth konexioa"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi konexioa"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth eta Wi-Fi konexioak"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Sistemako erabiltzaile-interfazearen konfiguratzailea"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Erakutsi txertatutako bateriaren ehunekoa"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Erakutsi bateria-mailaren ehunekoa egoera-barraren ikonoan, kargatzen ari ez denean"</string>
@@ -552,26 +573,27 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Desaktibatuta"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"Bateria-mailaren arabera jakinarazpenak kontrolatzeko aukerekin, 0 eta 5 bitarteko garrantzi-mailetan sailka ditzakezu aplikazioen jakinarazpenak. \n\n"<b>"5. maila"</b>" \n- Erakutsi jakinarazpenen zerrendaren goialdean. \n- Baimendu etetea pantaila osoko moduan zaudenean. \n- Agerrarazi beti jakinarazpenak. \n\n"<b>"4. maila"</b>" \n- Galarazi etetea pantaila osoko moduan zaudenean. \n- Agerrarazi beti jakinarazpenak. \n\n"<b>"3. maila"</b>" \n- Galarazi etetea pantaila osoko moduan zaudenean. \n- Ez agerrarazi jakinarazpenik inoiz. \n\n"<b>"2. maila"</b>" \n- Galarazi etetea pantaila osoko moduan zaudenean. \n- Ez agerrarazi jakinarazpenik inoiz. \n- Ez egin soinurik edo dardararik inoiz. \n\n"<b>"1. maila"</b>" \n- Galarazi etetea pantaila osoko moduan zaudenean. \n- Ez agerrarazi jakinarazpenik inoiz. \n- Ez egin soinurik edo dardararik inoiz. \n- Ezkutatu pantaila blokeatutik eta egoera-barratik. \n- Erakutsi jakinarazpenen zerrendaren behealdean. \n\n"<b>"0. maila"</b>" \n- Blokeatu aplikazioaren jakinarazpen guztiak."</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Jakinarazpenak"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"Aurrerantzean ez duzu jasoko horrelako jakinarazpenik"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"Jakinarazpenen <xliff:g id="NUMBER">%d</xliff:g> kategoria"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"Aplikazio honek ez du jakinarazpen-kategoriarik"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Ezin dira desaktibatu aplikazio honen jakinarazpenak"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="other">Aplikazio honen 1/<xliff:g id="NUMBER_1">%s</xliff:g> jakinarazpen-kategoria</item>
- <item quantity="one">Aplikazio honen 1/<xliff:g id="NUMBER_0">%s</xliff:g> jakinarazpen-kategoria</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> eta beste <xliff:g id="NUMBER_5">%3$d</xliff:g></item>
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> eta beste <xliff:g id="NUMBER_2">%3$d</xliff:g></item>
- </plurals>
+ <!-- no translation found for notification_channel_disabled (344536703863700565) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing (8945102997083836858) -->
+ <skip />
+ <!-- no translation found for inline_stop_button (4172980096860941033) -->
+ <skip />
+ <!-- no translation found for inline_keep_button (6665940297019018232) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing_app (1723113469580031041) -->
+ <skip />
+ <!-- no translation found for notification_unblockable_desc (1037434112919403708) -->
+ <skip />
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Ireki dira <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren jakinarazpenak kontrolatzeko aukerak"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Itxi dira <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren jakinarazpenak kontrolatzeko aukerak"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Onartu kanal honen jakinarazpenak"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"Kategoria guztiak"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Ezarpen gehiago"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Pertsonalizatu: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <!-- no translation found for notification_app_settings (420348114670768449) -->
+ <skip />
<string name="notification_done" msgid="5279426047273930175">"Eginda"</string>
+ <!-- no translation found for inline_undo (558916737624706010) -->
+ <skip />
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"jakinarazpena kontrolatzeko aukerak"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"jakinarazpena atzeratzeko aukerak"</string>
@@ -726,6 +748,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Zabaldu"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimizatu"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Itxi"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Ezarpenak"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Baztertzeko, arrastatu behera"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menua"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"Pantaila txiki gainjarrian dago <xliff:g id="NAME">%s</xliff:g>"</string>
@@ -754,7 +777,7 @@
<string name="tuner_right" msgid="6222734772467850156">"Eskuinera"</string>
<string name="tuner_menu" msgid="191640047241552081">"Menua"</string>
<string name="tuner_app" msgid="3507057938640108777">"<xliff:g id="APP">%1$s</xliff:g> aplikazioa"</string>
- <string name="notification_channel_alerts" msgid="4496839309318519037">"Abisuak"</string>
+ <string name="notification_channel_alerts" msgid="4496839309318519037">"Alertak"</string>
<string name="notification_channel_battery" msgid="5786118169182888462">"Bateria"</string>
<string name="notification_channel_screenshot" msgid="6314080179230000938">"Pantaila-argazkiak"</string>
<string name="notification_channel_general" msgid="4525309436693914482">"Mezu orokorrak"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index bb77daa..834397d8 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. برای بیصدا کردن ضربه بزنید."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"کنترلهای میزان صدای %s نشان داده شدند. برای نپذیرفتن انگشتتان را تند بکشید."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"کنترلهای صدا پنهان هستند"</string>
+ <string name="output_title" msgid="5355078100792942802">"خروجی رسانه"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"خروجی تماس تلفنی"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"دستگاهی پیدا نشد"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"دستگاهی پیدا نشد. <xliff:g id="SERVICE">%1$s</xliff:g> را روشن کنید"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"بلوتوث"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"بلوتوث و Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"تنظیمکننده واسط کاربری سیستم"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"نمایش درصد شارژ باتری جاسازی شده"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"نمایش درصد سطح باتری در نماد نوار وضعیت، هنگامی که باتری شارژ نمیشود"</string>
@@ -724,6 +731,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"بزرگ کردن"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"کوچک کردن"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"بستن"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"تنظیمات"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"برای نپذیرفتن، به پایین بکشید"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"منو"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> درحالت تصویر در تصویر است"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 3bebe4b..51a71de 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Mykistä napauttamalla."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Äänenvoimakkuuden säätimiä on näkyvissä (%s). Hylkää pyyhkäisemällä ylös."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Äänenvoimakkuuden säätimet piilotettiin."</string>
+ <string name="output_title" msgid="5355078100792942802">"Median äänentoisto"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Puhelun äänentoisto"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Laitteita ei löytynyt"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Laitteita ei löytynyt. Kokeile ottaa käyttöön <xliff:g id="SERVICE">%1$s</xliff:g>."</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth ja Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Näytä akun varaus kuvakkeessa"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Näyttää akun varausprosentin tilapalkin kuvakkeessa, kun laitetta ei ladata."</string>
@@ -724,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Laajenna"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Pienennä"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Sulje"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Hylkää vetämällä alas."</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Valikko"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> on kuva kuvassa ‑tilassa"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index a2dde85..16a4eac 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -33,7 +33,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"En cours"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Pile faible"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"La pile est faible. Activez le mode Économie d\'énergie."</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> restants"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Il reste <xliff:g id="PERCENTAGE">%s</xliff:g>, environ <xliff:g id="TIME">%s</xliff:g> en fonction de votre usage"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Il reste <xliff:g id="PERCENTAGE">%s</xliff:g>, environ <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> restants. La fonction Économie d\'énergie est activée."</string>
<string name="invalid_charger" msgid="4549105996740522523">"Chargement USB non compatible.\nVous devez utiliser le chargeur fourni."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"Le chargement par USB n\'est pas pris en charge."</string>
@@ -67,14 +70,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"L\'utilisateur actuellement connecté sur cet appareil ne peut pas activer le débogage USB. Pour utiliser cette fonctionnalité, l\'utilisateur principal doit se connecter."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoomer pour remplir l\'écran"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Étirer pour remplir l\'écran"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Capture d\'écran"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Enregistrement capture écran…"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Enregistrement capture écran…"</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Enregistrement de la capture d\'écran en cours…"</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Capture d\'écran réussie"</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Touchez pour afficher votre capture d\'écran."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Impossible de réaliser une capture d\'écran"</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Une erreur s\'est produite lors de l\'enregistrement de la saisie d\'écran."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Impossible d\'enregistrer la saisie d\'écran, car l\'espace de stockage est limité."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"Enregistrement de la capture d\'écran en cours…"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"Capture d\'écran enregistrée"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Touchez pour afficher votre capture d\'écran"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"Impossible de réaliser une capture d\'écran"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Une erreur s\'est produite lors de l\'enregistrement de la capture d\'écran"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Impossible d\'enregistrer la capture d\'écran, car l\'espace de stockage est limité"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"L\'application ou votre organisation n\'autorise pas les saisies d\'écran"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Options transfert fichiers USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Installer comme un lecteur multimédia (MTP)"</string>
@@ -505,6 +509,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Touchez pour couper le son."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Commandes de volume %s affichées. Faire glisser vers le haut pour ignorer."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Les commandes de volume sont masquées"</string>
+ <string name="output_title" msgid="5355078100792942802">"Sortie multimédia"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Sortie d\'appel téléphonique"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Aucun appareil trouvé"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Aucun appareil n\'a été trouvé. Essayez d\'activer le <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth et le Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Afficher le pourcentage intégré de charge"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Afficher le pourcentage correspondant au niveau de la pile dans l\'icône de la barre d\'état lorsque l\'appareil n\'est pas en charge."</string>
@@ -552,26 +563,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Désactivé"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"Avec les réglages avancés des notifications, vous pouvez définir un degré d\'importance de 0 à 5 pour les notifications d\'une application. \n\n"<b>"Niveau 5"</b>" \n- Afficher dans le haut de la liste des notifications \n- Autoriser les interruptions en mode plein écran \n- Toujours afficher les aperçus \n\n"<b>"Niveau 4"</b>" \n- Empêcher les interruptions en mode plein écran \n- Toujours afficher les aperçus \n\n"<b>"Niveau 3"</b>" \n- Empêcher les interruptions en mode plein écran \n- Ne jamais afficher les aperçus \n\n"<b>"Niveau 2"</b>" \n- Empêcher les interruptions en mode plein écran \n- Ne jamais afficher les aperçus \n- Ne pas autoriser les sons et les vibrations \n\n"<b>"Niveau 1"</b>" \n- Empêcher les interruptions en mode plein écran \n- Ne jamais afficher les aperçus \n- Ne pas autoriser les sons et les vibrations \n- Masquer de l\'écran de verrouillage et de la barre d\'état status bar \n- Afficher dans le bas de la liste des notifications \n\n"<b>"Level 0"</b>" \n- Bloquer toutes les notifications de l\'application"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"Vous ne recevrez plus ces notifications"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> catégories de notification"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"Cette application n\'a pas de catégories de notification"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Les notifications de cette application ne peuvent pas être désactivées"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="one">1 catégorie de notification sur <xliff:g id="NUMBER_1">%s</xliff:g> provenant de cette application</item>
- <item quantity="other">1 catégorie de notification sur <xliff:g id="NUMBER_1">%s</xliff:g> provenant de cette application</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> et <xliff:g id="NUMBER_5">%3$d</xliff:g> autre</item>
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> et <xliff:g id="NUMBER_5">%3$d</xliff:g> autres</item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"Vous ne verrez plus ces notifications"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"Continuer à afficher ces notifications?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"Arrêter les notifications"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Continuez à afficher"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuer à afficher les notifications de cette application?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"Ces notifications ne peuvent pas être désactivées"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Les paramètres des notifications pour <xliff:g id="APP_NAME">%1$s</xliff:g> sont ouverts"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Les paramètres des notifications pour <xliff:g id="APP_NAME">%1$s</xliff:g> sont fermés"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Autoriser les notifications de cette chaîne"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"Toutes les catégories"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Plus de paramètres"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Personnaliser : <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"Personnaliser"</string>
<string name="notification_done" msgid="5279426047273930175">"Terminé"</string>
+ <string name="inline_undo" msgid="558916737624706010">"Annuler"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"paramètres des notifications"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"options de répétition des notifications"</string>
@@ -726,6 +730,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Développer"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Réduire"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Fermer"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Paramètres"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Faire glisser vers le bas pour ignorer"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> est en mode d\'incrustation d\'image"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 21952ae..cc8bcba 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -33,7 +33,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"En cours"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Batterie faible"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Batterie faible : activez l\'économiseur de batterie"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> restants"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> – Temps restant en fonction de votre utilisation : environ <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> – Temps restant : environ <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> restants. L\'économiseur de batterie est activé."</string>
<string name="invalid_charger" msgid="4549105996740522523">"Chargement USB non disponible.\nVous devez utiliser le chargeur fourni."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"Chargeur USB non compatible."</string>
@@ -67,14 +70,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"L\'utilisateur actuellement connecté sur cet appareil ne peut pas activer le débogage USB. Pour utiliser cette fonctionnalité, l\'utilisateur principal doit se connecter."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoomer pour remplir l\'écran"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Étirer pour remplir l\'écran"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Capture d\'écran"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Enregistrement capture écran…"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Enregistrement de la capture d\'écran…"</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Enregistrement de la capture d\'écran en cours…"</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Capture d\'écran réussie"</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Appuyez pour afficher votre capture d\'écran."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Impossible de réaliser une capture d\'écran"</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Erreur lors de l\'enregistrement de la capture d\'écran."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Impossible d\'enregistrer la capture d\'écran, car l\'espace de stockage est limité."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"Enregistrement de la capture d\'écran…"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"Capture d\'écran enregistrée"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Appuyez pour afficher votre capture d\'écran"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"Impossible d\'effectuer une capture d\'écran"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Erreur lors de l\'enregistrement de la capture d\'écran"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Impossible d\'enregistrer la capture d\'écran, car l\'espace de stockage est limité"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Les captures d\'écran ne sont pas autorisées par l\'application ni par votre organisation"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Options transfert fichiers USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Installer en tant que lecteur multimédia (MTP)"</string>
@@ -494,6 +498,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"DTMF"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Accessibilité"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Appels"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"Sonnerie"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibreur"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"Silencieux"</string>
@@ -504,6 +509,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Appuyez pour ignorer."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Commandes de volume %s affichées. Faire glisser vers le haut pour ignorer."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Commandes de volume masquées"</string>
+ <string name="output_title" msgid="5355078100792942802">"Sortie multimédia"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Sortie de l\'appel téléphonique"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Aucun appareil détecté"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Aucun appareil détecté. Essayez d\'activer le <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth et Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Afficher le pourcentage intégré de la batterie"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Affichez le pourcentage correspondant au niveau de la batterie dans l\'icône de la barre d\'état lorsque l\'appareil n\'est pas en charge."</string>
@@ -551,26 +563,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Désactivé"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"Grâce aux commandes de gestion des notifications, vous pouvez définir le niveau d\'importance (compris entre 0 et 5) des notifications d\'une application. \n\n"<b>"Niveau 5"</b>" \n- Afficher en haut de la liste des notifications \n- Autoriser l\'interruption en plein écran \n- Toujours en aperçu \n\n"<b>"Niveau 4"</b>" \n- Empêcher l\'interruption en plein écran \n- Toujours en aperçu \n\n"<b>"Niveau 3"</b>" \n- Empêcher l\'interruption en plein écran \n- Jamais en aperçu \n\n"<b>"Niveau 2"</b>" \n- Empêcher l\'interruption en plein écran \n- Jamais en aperçu \n- Ne jamais émettre de signal sonore ni déclencher le vibreur \n\n"<b>"Niveau 1"</b>" \n- Empêcher l\'interruption en plein écran \n- Jamais en aperçu \n- Ne jamais émettre de signal sonore ni déclencher le vibreur \n- Masquer les notifications dans l\'écran de verrouillage et la barre d\'état \n- Afficher au bas de la liste des notifications \n\n"<b>"Niveau 0"</b>" \n- Bloquer toutes les notifications de l\'application"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"Vous ne recevrez plus ces notifications"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> catégories de notification"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"Cette application n\'a pas de catégories de notification"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Impossible de désactiver les notifications de cette application"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="one">1 catégorie de notification sur <xliff:g id="NUMBER_1">%s</xliff:g> provenant de cette application</item>
- <item quantity="other">1 catégorie de notification sur <xliff:g id="NUMBER_1">%s</xliff:g> provenant de cette application</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> et <xliff:g id="NUMBER_5">%3$d</xliff:g> autre</item>
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> et <xliff:g id="NUMBER_5">%3$d</xliff:g> autres</item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"Vous ne recevrez plus ces notifications"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"Continuer d\'afficher ces notifications ?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"Arrêter les notifications"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Continuer d\'afficher les notifications"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuer d\'afficher les notifications de cette application ?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"Ces notifications ne peuvent pas être désactivées"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Les commandes de notification sont disponibles pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Les commandes de notification sont indisponibles pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Autoriser les notifications pour cette chaîne"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"Toutes les catégories"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Plus de paramètres"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Personnaliser : <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"Personnaliser"</string>
<string name="notification_done" msgid="5279426047273930175">"Terminé"</string>
+ <string name="inline_undo" msgid="558916737624706010">"Annuler"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> : <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"paramètres des notifications"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"options de répétition des notifications"</string>
@@ -725,6 +730,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Développer"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Réduire"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Fermer"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Paramètres"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Faire glisser vers le bas pour ignorer"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> est en mode Picture-in-picture"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 9fbcdec..987ea59 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -505,6 +505,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Toca para silenciar."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Estanse mostrando os controis de volume de %s. Pasa o dedo cara a arriba para ignoralos."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Ocultáronse os controis de volume"</string>
+ <string name="output_title" msgid="5355078100792942802">"Saída multimedia"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Saída de chamadas telefónicas"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Non se atopou ningún dispositivo"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Non se atopou ningún dispositivo. Proba a activar <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wifi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth e wifi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Configurador da IU do sistema"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Mostrar porcentaxe de batería inserida"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Mostrar porcentaxe do nivel de batería na icona da barra de estado cando non está en carga"</string>
@@ -726,6 +733,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Despregar"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimizar"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Pechar"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Arrastra cara abaixo para ignorar"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menú"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> está na pantalla superposta"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 418f31e1..22966a7 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -33,7 +33,13 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"ચાલુ"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"નોટિફિકેશનો"</string>
<string name="battery_low_title" msgid="6456385927409742437">"બૅટરી ઓછી છે"</string>
+ <!-- no translation found for battery_low_title_hybrid (6268991275887381595) -->
+ <skip />
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> બાકી"</string>
+ <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) -->
+ <skip />
+ <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) -->
+ <skip />
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> બાકી. બૅટરી સેવર ચાલુ છે."</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB ચાર્જિંગ સમર્થિત નથી.\nફક્ત આપવામાં આવેલ ચાર્જરનો ઉપયોગ કરો."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"USB ચાર્જિંગ સમર્થિત નથી."</string>
@@ -67,14 +73,22 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"હાલમાં આ ઉપકરણમાં સાઇન ઇન થયેલ વપરાશકર્તા USB ડિબગીંગ ચાલુ કરી શકતા નથી. આ સુવિધાનો ઉપયોગ કરવા માટે પ્રાથમિક વપરાશકર્તા પર સ્વિચ કરો."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"સ્ક્રીન ભરવા માટે ઝૂમ કરો"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"સ્ક્રીન ભરવા માટે ખેંચો"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"સ્ક્રીનશોટ સાચવી રહ્યું છે…"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"સ્ક્રીનશોટ સાચવી રહ્યું છે…"</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"સ્ક્રીનશોટ સાચવવામાં આવી રહ્યો છે."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"સ્ક્રીનશોટ કેપ્ચર કર્યો."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"તમારા સ્ક્રીનશૉટને જોવા માટે ટૅપ કરો."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"સ્ક્રીનશોટ કેપ્ચર કરી શકાયો નથી."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"સ્ક્રીનશૉટ સાચવવામાં સમસ્યા આવી."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"મર્યાદિત સંગ્રહ સ્થાનને કારણે સ્ક્રીનશોટ સાચવી શકાતો નથી."</string>
+ <!-- no translation found for screenshot_saving_text (2545047868936087248) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_title (5637073968117370753) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_text (7574667448002050363) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_title (9096484883063264803) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) -->
+ <skip />
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"ઍપ્લિકેશન કે તમારી સંસ્થા દ્વારા સ્ક્રીનશૉટ લેવાની મંજૂરી નથી"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB ફાઇલ ટ્રાન્સફર વિકલ્પો"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"મીડિયા પ્લેયર તરીકે માઉન્ટ કરો (MTP)"</string>
@@ -492,8 +506,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"બ્લૂટૂથ"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"દ્વિ બહુ ટોન આવર્તન"</string>
<string name="stream_accessibility" msgid="301136219144385106">"ઍક્સેસિબિલિટી"</string>
- <!-- no translation found for ring_toggle_title (3281244519428819576) -->
- <skip />
+ <string name="ring_toggle_title" msgid="3281244519428819576">"કૉલ"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"રિંગ કરો"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"વાઇબ્રેટ"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"મ્યૂટ કરો"</string>
@@ -504,6 +517,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. મ્યૂટ કરવા માટે ટૅપ કરો."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s વૉલ્યૂમ નિયંત્રણ બતાવ્યાં. છોડી દેવા માટે સ્વાઇપ કરો."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"વૉલ્યૂમ નિયંત્રણ છુપાવ્યાં"</string>
+ <string name="output_title" msgid="5355078100792942802">"મીડિયાનું આઉટપુટ"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"ફોન કૉલનો આઉટપુટ"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"કોઈ ઉપકરણો મળ્યા નથી"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"કોઈ ઉપકરણો મળ્યા નથી. <xliff:g id="SERVICE">%1$s</xliff:g>ને ચાલુ કરવાનો પ્રયાસ કરો"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"બ્લૂટૂથ"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"વાઇ-ફાઇ"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"બ્લૂટૂથ અને વાઇ-ફાઇ"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"સિસ્ટમ UI ટ્યૂનર"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"એમ્બેડ કરેલ બૅટરી ટકા બતાવો"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"જ્યારે ચાર્જ ન થઈ રહ્યું હોય ત્યારે સ્ટેટસ બાર આયકનની અંદર બૅટરી સ્તર ટકા બતાવો"</string>
@@ -551,26 +571,27 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"બંધ"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"પાવર સૂચના નિયંત્રણો સાથે, તમે ઍપની સૂચનાઓ માટે 0 થી 5 સુધીના મહત્વના સ્તરને સેટ કરી શકો છો. \n\n"<b>"સ્તર 5"</b>" \n- સૂચના સૂચિની ટોચ પર બતાવો \n- પૂર્ણ સ્ક્રીન અવરોધની મંજૂરી આપો \n- હંમેશાં ત્વરિત દૃષ્ટિ કરો \n\n"<b>"સ્તર 4"</b>" \n- પૂર્ણ સ્ક્રીન અવરોધ અટકાવો \n- હંમેશાં ત્વરિત દૃષ્ટિ કરો \n\n"<b>"સ્તર 3"</b>" \n- પૂર્ણ સ્ક્રીન અવરોધ અટકાવો \n- ક્યારેય ત્વરિત દૃષ્ટિ કરશો નહીં \n\n"<b>"સ્તર 2"</b>" \n- પૂર્ણ સ્ક્રીન અવરોધ અટકાવો \n- ક્યારેય ત્વરિત દૃષ્ટિ કરશો નહીં \n- ક્યારેય અવાજ અથવા વાઇબ્રેટ કરશો નહીં \n\n"<b>"સ્તર 1"</b>" \n- પૂર્ણ સ્ક્રીન અવરોધની મંજૂરી આપો \n- ક્યારેય ત્વરિત દૃષ્ટિ કરશો નહીં \n- ક્યારેય અવાજ અથવા વાઇબ્રેટ કરશો નહીં \n- લૉક સ્ક્રીન અને સ્ટેટસ બારથી છુપાવો \n- સૂચના સૂચિના તળિયા પર બતાવો \n\n"<b>"સ્તર 0"</b>" \n- ઍપની તમામ સૂચનાઓને બ્લૉક કરો"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"નોટિફિકેશનો"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"તમને હવે આ સૂચનાઓ મળશે નહીં"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> સૂચના કૅટેગરીઓ"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"આ ઍપ્લિકેશનમાં સૂચના કૅટેગરી નથી"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"આ ઍપ્લિકેશનની સૂચનાઓ બંધ કરી શકાતી નથી"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="one">આ ઍપ્લિકેશનમાંની <xliff:g id="NUMBER_1">%s</xliff:g> સૂચના કૅટેગરીમાંથી 1</item>
- <item quantity="other">આ ઍપ્લિકેશનમાંની <xliff:g id="NUMBER_1">%s</xliff:g> સૂચના કૅટેગરીમાંથી 1</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> અને અન્ય <xliff:g id="NUMBER_5">%3$d</xliff:g></item>
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> અને અન્ય <xliff:g id="NUMBER_5">%3$d</xliff:g></item>
- </plurals>
+ <!-- no translation found for notification_channel_disabled (344536703863700565) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing (8945102997083836858) -->
+ <skip />
+ <!-- no translation found for inline_stop_button (4172980096860941033) -->
+ <skip />
+ <!-- no translation found for inline_keep_button (6665940297019018232) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing_app (1723113469580031041) -->
+ <skip />
+ <!-- no translation found for notification_unblockable_desc (1037434112919403708) -->
+ <skip />
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> માટે સૂચના નિયંત્રણો ચાલુ છે"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> માટે સૂચના નિયંત્રણો બંધ છે"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"આ ચૅનલની સૂચનાઓને મંજૂરી આપો"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"બધી કૅટેગરી"</string>
<string name="notification_more_settings" msgid="816306283396553571">"વધુ સેટિંગ્સ"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"કસ્ટમાઇઝ કરો: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <!-- no translation found for notification_app_settings (420348114670768449) -->
+ <skip />
<string name="notification_done" msgid="5279426047273930175">"થઈ ગયું"</string>
+ <!-- no translation found for inline_undo (558916737624706010) -->
+ <skip />
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"સૂચના નિયંત્રણો"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"સૂચના સ્નૂઝ કરવાના વિકલ્પો"</string>
@@ -725,6 +746,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"વિસ્તૃત કરો"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"નાનું કરો"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"બંધ કરો"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"સેટિંગ"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"છોડી દેવા માટે નીચે ખેંચો"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"મેનૂ"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> ચિત્રમાં-ચિત્રની અંદર છે"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 1acd6e7..27ed38c 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -33,7 +33,13 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"ऑनगोइंग"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"सूचनाएं"</string>
<string name="battery_low_title" msgid="6456385927409742437">"बैटरी कम है"</string>
+ <!-- no translation found for battery_low_title_hybrid (6268991275887381595) -->
+ <skip />
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> शेष"</string>
+ <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) -->
+ <skip />
+ <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) -->
+ <skip />
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> बैटरी बची है. बैटरी सेवर चालू है."</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB चार्जिंग समर्थित नहीं है.\nकेवल आपूर्ति किए गए चार्जर का उपयोग करें."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"USB चार्जिंग समर्थित नहीं है."</string>
@@ -67,14 +73,22 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"अभी इस डिवाइस में जिस उपयोगकर्ता ने साइन इन किया है, वो USB डीबगिंग चालू नहीं कर सकता. इस सुविधा का इस्तेमाल करने के लिए, प्राथमिक उपयोगकर्ता में बदलें."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"स्क्रीन भरने के लिए ज़ूम करें"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"स्क्रीन भरने के लिए खींचें"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"स्क्रीनशॉट सहेजा जा रहा है..."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"स्क्रीनशॉट सहेजा जा रहा है..."</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"स्क्रीनशॉट सहेजा जा रहा है."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"स्क्रीनशॉट कैप्चर किया गया."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"अपना स्क्रीनशॉट देखने के लिए टैप करें."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"स्क्रीनशॉट को कैप्चर नहीं किया जा सका."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"स्क्रीनशॉट सहेजने में समस्या आई"</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"मेमोरी में जगह कम होने की वजह से स्क्रीनशॉट सेव नहीं किया जा सकता."</string>
+ <!-- no translation found for screenshot_saving_text (2545047868936087248) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_title (5637073968117370753) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_text (7574667448002050363) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_title (9096484883063264803) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) -->
+ <skip />
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"ऐप्लिकेशन या आपका संगठन स्क्रीनशॉट लेने की अनुमति नहीं देता"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB फ़ाइल स्थानांतरण विकल्प"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"मीडिया प्लेयर के रूप में माउंट करें (MTP)"</string>
@@ -492,8 +506,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"ब्लूटूथ"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"दोहरी बहु टोन आवृत्ति"</string>
<string name="stream_accessibility" msgid="301136219144385106">"सुलभता"</string>
- <!-- no translation found for ring_toggle_title (3281244519428819576) -->
- <skip />
+ <string name="ring_toggle_title" msgid="3281244519428819576">"कॉल"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"आवाज़ चालू है"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"कंपन (वाइब्रेशन)"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"आवाज़ बंद है"</string>
@@ -504,6 +517,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. म्यूट करने के लिए टैप करें."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s वॉल्यूम नियंत्रण दिखाए गए हैं. खारिज करने के लिए स्वाइप करें."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"वॉल्यूम नियंत्रण छिपे हुए हैं"</string>
+ <string name="output_title" msgid="5355078100792942802">"मीडिया आउटपुट"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"फ़ोन कॉल का आउटपुट"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"कोई डिवाइस नहीं मिला"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"कोई डिवाइस नहीं मिला. <xliff:g id="SERVICE">%1$s</xliff:g> को चालू करें"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"ब्लूटूथ"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"वाई-फ़ाई"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"ब्लूटूथ और वाई-फ़ाई"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"सिस्टम यूज़र इंटरफ़ेस (यूआई) ट्यूनर"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"एम्बेड किया गया बैटरी प्रतिशत दिखाएं"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"जब चार्ज नहीं किया जा रहा हो तब स्टेटस बार आइकॉन में बैटरी लेवल का प्रतिशत दिखाएं"</string>
@@ -551,26 +571,27 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"बंद"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"पावर सूचना नियंत्रण के ज़रिये, आप किसी ऐप की सूचना को उसकी अहमियत के हिसाब से 0 से 5 के लेवल पर सेट कर सकते हैं.\n\n"<b>"लेवल 5"</b>" \n- सूचना सूची में सबसे ऊपर दिखाएं \n- पूरे स्क्रीन को ढंकने की अनुमति दें \n- लगातार देखते रहें \n\n"<b>" लेवल 4"</b>" \n- पूरे स्क्रीन को ढंकें \n- लगातार देखते रहें \n\n"<b>"लेवल 3"</b>" \n- पूरे स्क्रीन को ढंकने से रोकें \n-कभी भी न देखें \n\n"<b>"लेवल 2"</b>" \n- पूरे स्क्रीन को ढंकने से रोकें \n- कभी भी देखें \n- कभी भी आवाज़ या कंपन (वाइब्रेशन) न करें \n\n"<b>"लेवल 1"</b>" \n- पूरे स्क्रीन को ढंकने से रोकें \n- कभी भी न देखें \n- कभी भी आवाज़ या कंपन (वाइब्रेशन) न करें \n- लॉक स्क्रीन और स्टेटस बार से छिपाएं \n- सूचना सूची के नीचे दिखाएं \n\n"<b>"लेवल 0"</b>" \n- ऐप्लिकेशन की सभी सूचनाएं रोक दें"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"सूचना"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"अब आपको ये सूचनाएं नहीं मिलेंगी"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"सूचना की <xliff:g id="NUMBER">%d</xliff:g> श्रेणियां"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"इस ऐप्लिकेशन में सूचना श्रेणियां नहीं हैं"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"इस ऐप की सूचनाएं बंद नहीं की जा सकती"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="one">इस ऐप की <xliff:g id="NUMBER_1">%s</xliff:g> सूचना श्रेणियों में से 1 श्रेणी</item>
- <item quantity="other">इस ऐप की <xliff:g id="NUMBER_1">%s</xliff:g> सूचना श्रेणियों में से 1 श्रेणी</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> और <xliff:g id="NUMBER_5">%3$d</xliff:g> अन्य</item>
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> और <xliff:g id="NUMBER_5">%3$d</xliff:g> अन्य</item>
- </plurals>
+ <!-- no translation found for notification_channel_disabled (344536703863700565) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing (8945102997083836858) -->
+ <skip />
+ <!-- no translation found for inline_stop_button (4172980096860941033) -->
+ <skip />
+ <!-- no translation found for inline_keep_button (6665940297019018232) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing_app (1723113469580031041) -->
+ <skip />
+ <!-- no translation found for notification_unblockable_desc (1037434112919403708) -->
+ <skip />
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> के लिए सूचना नियंत्रण चालू हैं"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> के लिए सूचना नियंत्रण बंद हैं"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"इस चैनल से सूचना की पाने की मंज़ूरी दें"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"सभी श्रेणियां"</string>
<string name="notification_more_settings" msgid="816306283396553571">"और सेटिंग"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"अपनी पसंद के मुताबिक बनाएं: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <!-- no translation found for notification_app_settings (420348114670768449) -->
+ <skip />
<string name="notification_done" msgid="5279426047273930175">"हो गया"</string>
+ <!-- no translation found for inline_undo (558916737624706010) -->
+ <skip />
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"सूचना नियंत्रण"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"सूचना को स्नूज़ (थोड़ी देर के लिए चुप करना) करने के विकल्प"</string>
@@ -725,6 +746,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"विस्तार करें"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"छोटा करें"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"बंद करें"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"सेटिंग"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"खारिज करने के लिए नीचे खींचें और छोड़ें"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"मेन्यू"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> पिक्चर में पिक्चर के अंदर है"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 7ba9111..7395b66 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -505,6 +505,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Dodirnite da biste isključili zvuk."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s kontrole glasnoće prikazane. Kliznite prstom prema gore da biste ih odbacili."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Kontrole glasnoće skrivene"</string>
+ <string name="output_title" msgid="5355078100792942802">"Medijski izlaz"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Izlaz telefonskih poziva"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Nije pronađen nijedan uređaj"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Nije pronađen nijedan uređaj. Pokušajte uključiti uslugu <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth i Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Ugađanje korisničkog sučelja sustava"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Prikaži ugrađeni postotak baterije"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Prikazivanje postotka razine baterije na ikoni trake statusa kada se ne puni"</string>
@@ -730,6 +737,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Proširivanje"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimiziraj"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Zatvori"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Povucite prema dolje da biste odbacili"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Izbornik"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> jest na slici u slici"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 8aa2c71..87ca49a 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Koppintson a némításhoz."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"A(z) %s hangvezérlői megjelenítve. Az elvetéshez húzza felfelé az ujját."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Hangvezérlők elrejtve"</string>
+ <string name="output_title" msgid="5355078100792942802">"Médiakimenet"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Telefonhívás-kimenet"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Nem találhatók eszközök"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Nem találhatók eszközök. Próbálja meg a(z) <xliff:g id="SERVICE">%1$s</xliff:g> bekapcsolásával"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth és Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Kezelőfelület-hangoló"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"A beépített akkumulátor töltöttségi szintjének megjelenítése"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Az akkumulátor töltöttségi szintjének megjelenítése az állapotsori ikonban, amikor az eszköz nem töltődik"</string>
@@ -724,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Kibontás"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Kis méret"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Bezárás"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Elvetéshez húzza lefelé"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menü"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"A(z) <xliff:g id="NAME">%s</xliff:g> kép a képben funkciót használ"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 2841074..b6172c8 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s։ Հպեք՝ ձայնը անջատելու համար։"</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s ձայնի ուժգնության կառավարները ցուցադրված են: Մատը սահեցրեք վերև՝ փակելու համար:"</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Ձայնի ուժգնության կառավարները թաքցված են"</string>
+ <string name="output_title" msgid="5355078100792942802">"Մեդիա արտածում"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Հեռախոսազանգի հնչեցում"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Սարքեր չեն գտնվել"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Սարքեր չեն գտնվել: Միացրեք <xliff:g id="SERVICE">%1$s</xliff:g>-ը"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth-ը և Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Համակարգի ՕՄ-ի կարգավորիչ"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Ցուցադրել ներկառուցված մարտկոցի տոկոսայնությունը"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Ցուցադրել մարտկոցի լիցքավորման տոկոսայնությունը կարգավիճակի գոտու պատկերակի վրա, երբ այն չի լիցքավորվում"</string>
@@ -724,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Ընդարձակել"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Ծալել"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Փակել"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Քաշեք վար՝ փակելու համար"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Ընտրացանկ"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g>-ը «Նկար նկարի մեջ» ռեժիմում է"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index f8604e3..6d08308 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -33,7 +33,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Berkelanjutan"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifikasi"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Baterai lemah"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Baterai hampir habis. Aktifkan Penghemat Baterai"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"Tersisa <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Sisa <xliff:g id="PERCENTAGE">%s</xliff:g>, kira-kira <xliff:g id="TIME">%s</xliff:g> lagi berdasarkan penggunaan Anda"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Sisa <xliff:g id="PERCENTAGE">%s</xliff:g>, kira-kira <xliff:g id="TIME">%s</xliff:g> lagi"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Tersisa <xliff:g id="PERCENTAGE">%s</xliff:g>. Penghemat Baterai aktif."</string>
<string name="invalid_charger" msgid="4549105996740522523">"Pengisian daya USB tidak didukung.\nGunakan hanya pengisi daya yang disediakan."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"Pengisian daya USB tidak didukung."</string>
@@ -67,14 +70,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Pengguna yang sedang login ke perangkat ini tidak dapat mengaktifkan proses debug USB. Beralihlah ke pengguna utama untuk menggunakan fitur ini."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Perbesar utk mengisi layar"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Rentangkn utk mngisi layar"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Menyimpan screenshot..."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Menyimpan screenshot..."</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Screenshot sedang disimpan."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot diambil."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Ketuk untuk melihat screenshot."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Tidak dapat mengambil screenshot."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Terjadi masalah saat menyimpan screenshot."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Tidak dapat menyimpan screenshot karena ruang penyimpanan terbatas."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"Screenshot sedang disimpan"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"Screenshot disimpan"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Tap untuk melihat screenshot"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"Tidak dapat mengambil screenshot"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Terjadi masalah saat menyimpan screenshot"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Tidak dapat menyimpan screenshot karena ruang penyimpanan terbatas"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Mengambil screenshot tidak diizinkan oleh aplikasi atau organisasi"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opsi transfer file USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Pasang sebagai pemutar media (MTP)"</string>
@@ -492,6 +496,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Frekuensi multinada ganda"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Aksesibilitas"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Telepon"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"Dering"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Getar"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"Nonaktifkan"</string>
@@ -502,6 +507,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Tap untuk menonaktifkan."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Kontrol volume %s ditampilkan. Geser ke atas untuk menutup."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Kontrol volume disembunyikan"</string>
+ <string name="output_title" msgid="5355078100792942802">"Keluaran media"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Keluaran panggilan telepon"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Perangkat tidak ditemukan"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Tidak ditemukan perangkat. Coba aktifkan <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth dan Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Penyetel Antarmuka Pengguna Sistem"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Tampilkan persentase baterai yang tersemat"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Tampilkan persentase tingkat baterai dalam ikon bilah status saat tidak mengisi daya"</string>
@@ -549,26 +561,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Nonaktif"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"Dengan kontrol notifikasi daya, Anda dapt menyetel level kepentingan notifikasi aplikasi dari 0 sampai 5. \n\n"<b>"Level 5"</b>" \n- Muncul di atas daftar notifikasi \n- Izinkan interupsi layar penuh \n- Selalu intip pesan \n\n"<b>"Level 4"</b>" \n- Jangan interupsi layar penuh \n- Selalu intip pesan \n\n"<b>"Level 3"</b>" \n- Jangan interupsi layar penuh \n- Tak pernah intip pesan \n\n"<b>"Level 2"</b>" \n- Jangan interupsi layar penuh \n- Tak pernah intip pesan \n- Tanpa suara dan getaran \n\n"<b>"Level 1"</b>" \n- Jangan interupsi layar penuh \n- Tak pernah intip pesan \n- Tanpa suara atau getaran \n- Sembunyikan dari layar kunci dan bilah status \n- Muncul di bawah daftar notifikasi \n\n"<b>"Level 0"</b>" \n- Blokir semua notifikasi dari aplikasi"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notifikasi"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"Anda tidak akan mendapatkan notifikasi ini lagi"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> kategori notifikasi"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"Aplikasi ini tidak memiliki kategori notifikasi"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Notifikasi dari aplikasi ini tidak dapat dinonaktifkan"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="other">1 dari <xliff:g id="NUMBER_1">%s</xliff:g> kategori notifikasi dari aplikasi ini</item>
- <item quantity="one">1 dari <xliff:g id="NUMBER_0">%s</xliff:g> kategori notifikasi dari aplikasi ini</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, dan <xliff:g id="NUMBER_5">%3$d</xliff:g> lainnya</item>
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>, dan <xliff:g id="NUMBER_2">%3$d</xliff:g> lainnya</item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"Anda tidak akan melihat notifikasi ini lagi"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"Terus tampilkan notifikasi ini?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"Hentikan notifikasi"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Terus tampilkan"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"Terus tampilkan notifikasi dari aplikasi ini?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"Notifikasi ini tidak dapat dinonaktifkan"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Kontrol notifikasi untuk <xliff:g id="APP_NAME">%1$s</xliff:g> dibuka"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Kontrol notifikasi untuk <xliff:g id="APP_NAME">%1$s</xliff:g> ditutup"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Izinkan notifikasi dari saluran ini"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"Semua Kategori"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Setelan lainnya"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Sesuaikan: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"Sesuaikan"</string>
<string name="notification_done" msgid="5279426047273930175">"Selesai"</string>
+ <string name="inline_undo" msgid="558916737624706010">"Urungkan"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"kontrol notifikasi"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"opsi tunda notifikasi"</string>
@@ -723,6 +728,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Luaskan"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimalkan"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Tutup"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Setelan"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Seret ke bawah untuk menutup"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> adalah picture-in-picture"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 66530bd..35bf451 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Ýttu til að þagga."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s stýringar fyrir hljóðstyrk sýnilegar. Strjúktu upp til að hunsa."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Stýringar fyrir hljóðstyrk faldar"</string>
+ <string name="output_title" msgid="5355078100792942802">"Margmiðlunarúttak"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Úttak símtals"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Engin tæki fundust"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Engin tæki fundust. Prófaðu að kveikja á <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth og Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Fínstillingar kerfisviðmóts"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Sýna innfellda rafhlöðustöðu"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Sýna rafhlöðustöðuna í stöðustikunni þegar tækið er ekki í hleðslu"</string>
@@ -724,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Stækka"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minnka"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Loka"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Draga niður til að hunsa"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Valmynd"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> er með mynd í mynd"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index c15e4b6..642a8dca 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -33,7 +33,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"In corso"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifiche"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Batteria quasi scarica"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Batteria quasi scarica. Attiva Risparmio energetico"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> rimanente"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante. Tempo rimanente in base al tuo utilizzo: <xliff:g id="TIME">%s</xliff:g> circa"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante. Tempo rimanente: <xliff:g id="TIME">%s</xliff:g> circa"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> rimanente. Risparmio energetico attivo."</string>
<string name="invalid_charger" msgid="4549105996740522523">"Ricarica tramite USB non supportata.\nUtilizza solo il caricatore in dotazione."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"Ricarica tramite USB non supportata."</string>
@@ -67,14 +70,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"L\'utente che ha eseguito l\'accesso a questo dispositivo non può attivare il debug USB. Per utilizzare questa funzione, passa all\'utente principale."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom per riempire schermo"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Estendi per riemp. schermo"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Salvataggio screenshot..."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Salvataggio screenshot..."</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Screenshot in corso di salvataggio."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot acquisito."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Tocca per visualizzare lo screenshot."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Impossibile acquisire lo screenshot."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Si è verificato un problema durante il salvataggio dello screenshot."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Impossibile salvare lo screenshot a causa dello spazio di archiviazione limitato."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"Salvataggio screenshot…"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"Screenshot salvato"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Tocca per visualizzare lo screenshot"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"Impossibile acquisire lo screenshot"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Si è verificato un problema durante il salvataggio dello screenshot"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Impossibile salvare lo screenshot a causa dello spazio di archiviazione limitato"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"L\'acquisizione di screenshot non è consentita dall\'app o dall\'organizzazione"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opzioni trasferimento file USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Monta come lettore multimediale (MTP)"</string>
@@ -494,9 +498,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Frequenza multipla dual tone"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Accessibilità"</string>
- <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Suoneria"</string>
- <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrazione"</string>
- <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Disattiva audio"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Chiamate"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Attiva suoneria"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Attiva vibrazione"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Disattiva suoneria"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Tocca per riattivare l\'audio."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tocca per attivare la vibrazione. L\'audio dei servizi di accessibilità può essere disattivato."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Tocca per disattivare l\'audio. L\'audio dei servizi di accessibilità può essere disattivato."</string>
@@ -504,6 +509,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Tocca per disattivare l\'audio."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s comandi del volume mostrati. Fai scorrere verso l\'alto per ignorare."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Comandi del volume nascosti"</string>
+ <string name="output_title" msgid="5355078100792942802">"Uscita contenuti multimediali"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Uscita telefonate"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Nessun dispositivo trovato"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Nessun dispositivo trovato. Prova ad attivare <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth e Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Ottimizzatore UI di sistema"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Mostra percentuale batteria incorporata"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Mostra la percentuale di carica della batteria nell\'icona della barra di stato quando non è in carica"</string>
@@ -551,26 +563,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Off"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"I controlli di gestione delle notifiche ti consentono di impostare un livello di importanza compreso tra 0 e 5 per le notifiche di un\'app. \n\n"<b>"Livello 5"</b>" \n- Mostra in cima all\'elenco di notifiche \n- Consenti l\'interruzione a schermo intero \n- Visualizza sempre \n\n"<b>"Livello 4"</b>" \n- Impedisci l\'interruzione a schermo intero \n- Visualizza sempre \n\n"<b>"Livello 3"</b>" \n- Impedisci l\'interruzione a schermo intero \n- Non visualizzare mai \n\n"<b>"Livello 2"</b>" \n- Impedisci l\'interruzione a schermo intero \n- Non visualizzare mai \n- Non emettere mai suoni e vibrazioni \n\n"<b>"Livello 1"</b>" \n- Impedisci l\'interruzione a schermo intero \n- Non visualizzare mai \n- Non emettere mai suoni e vibrazioni \n- Nascondi da schermata di blocco e barra di stato \n- Mostra in fondo all\'elenco di notifiche \n\n"<b>"Livello 0"</b>" \n- Blocca tutte le notifiche dell\'app"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notifiche"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"Non riceverai più queste notifiche"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> categorie di notifica"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"Questa app non ha categorie di notifica"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Le notifiche di quest\'app non possono essere disattivate"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="other">1 categoria di notifica su <xliff:g id="NUMBER_1">%s</xliff:g> di questa app</item>
- <item quantity="one">1 categoria di notifica su <xliff:g id="NUMBER_0">%s</xliff:g> di questa app</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> e altri <xliff:g id="NUMBER_5">%3$d</xliff:g></item>
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> e <xliff:g id="NUMBER_2">%3$d</xliff:g> altro</item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"Non vedrai più queste notifiche"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"Continuare a ricevere queste notifiche?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"Interrompi la ricezione di notifiche"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Continua a mostrare"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuare a ricevere notifiche da questa app?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"Queste notifiche non possono essere disattivate"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Controlli di gestione delle notifiche per <xliff:g id="APP_NAME">%1$s</xliff:g> aperti"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Controlli di gestione delle notifiche per <xliff:g id="APP_NAME">%1$s</xliff:g> chiusi"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Consenti le notifiche di questo canale"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"Tutte le categorie"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Altre impostazioni"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Personalizza: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"Personalizza"</string>
<string name="notification_done" msgid="5279426047273930175">"Fine"</string>
+ <string name="inline_undo" msgid="558916737624706010">"Annulla"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"gestione delle notifiche"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"opzioni di posticipazione notifiche"</string>
@@ -725,6 +730,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Espandi"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Riduci a icona"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Chiudi"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Impostazioni"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Trascina verso il basso per ignorare"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> è in Picture in picture"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index cbbffa9..276fa33 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -31,9 +31,9 @@
<item quantity="other">%d מסכים ב’סקירה‘</item>
<item quantity="one">מסך אחד ב’סקירה‘</item>
</plurals>
- <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"אין התראות"</string>
+ <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"אין הודעות"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"מתמשך"</string>
- <string name="status_bar_latest_events_title" msgid="6594767438577593172">"התראות"</string>
+ <string name="status_bar_latest_events_title" msgid="6594767438577593172">"הודעות"</string>
<string name="battery_low_title" msgid="6456385927409742437">"עוצמת הסוללה נמוכה"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"נותרו <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"נותרו <xliff:g id="PERCENTAGE">%s</xliff:g>. הופעלה תכונת החיסכון בסוללה."</string>
@@ -49,7 +49,7 @@
<string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"סיבוב אוטומטי של המסך"</string>
<string name="status_bar_settings_mute_label" msgid="554682549917429396">"השתק"</string>
<string name="status_bar_settings_auto_brightness_label" msgid="511453614962324674">"אוטומטי"</string>
- <string name="status_bar_settings_notifications" msgid="397146176280905137">"התראות"</string>
+ <string name="status_bar_settings_notifications" msgid="397146176280905137">"הודעות"</string>
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth קשור"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"הגדר שיטות קלט"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"מקלדת פיזית"</string>
@@ -166,7 +166,7 @@
<string name="accessibility_battery_level" msgid="7451474187113371965">"<xliff:g id="NUMBER">%d</xliff:g> אחוזים של סוללה."</string>
<string name="accessibility_battery_level_charging" msgid="1147587904439319646">"טעינת סוללה, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> אחוז."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"הגדרות מערכת"</string>
- <string name="accessibility_notifications_button" msgid="4498000369779421892">"התראות"</string>
+ <string name="accessibility_notifications_button" msgid="4498000369779421892">"הודעות"</string>
<string name="accessibility_overflow_action" msgid="5681882033274783311">"הצגת כל ההודעות"</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"נקה התראה"</string>
<string name="accessibility_gps_enabled" msgid="3511469499240123019">"GPS מופעל."</string>
@@ -248,7 +248,7 @@
<string name="gps_notification_searching_text" msgid="8574247005642736060">"מחפש GPS"</string>
<string name="gps_notification_found_text" msgid="4619274244146446464">"מיקום מוגדר על ידי GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"בקשות מיקום פעילות"</string>
- <string name="accessibility_clear_all" msgid="5235938559247164925">"נקה את כל ההתראות."</string>
+ <string name="accessibility_clear_all" msgid="5235938559247164925">"נקה את כל ההודעות."</string>
<string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<plurals name="notification_group_overflow_description" formatted="false" msgid="4579313201268495404">
<item quantity="two">יש בפנים עוד <xliff:g id="NUMBER_1">%s</xliff:g> הודעות.</item>
@@ -371,7 +371,7 @@
<string name="interruption_level_priority" msgid="6426766465363855505">"עדיפות בלבד"</string>
<string name="interruption_level_alarms" msgid="5226306993448328896">"התראות בלבד"</string>
<string name="interruption_level_none_twoline" msgid="3957581548190765889">"שקט\nמוחלט"</string>
- <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"התראות בעדיפות\nבלבד"</string>
+ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"הודעות בעדיפות\nבלבד"</string>
<string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"התראות\nבלבד"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"טוען (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> עד לסיום)"</string>
<string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"בטעינה מהירה (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> עד למילוי)"</string>
@@ -470,7 +470,7 @@
<string name="keyguard_indication_trust_granted" msgid="4985003749105182372">"הנעילה בוטלה על ידי <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="keyguard_indication_trust_managed" msgid="8319646760022357585">"<xliff:g id="TRUST_AGENT">%1$s</xliff:g> פועל"</string>
<string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"המכשיר יישאר נעול עד שתבטל את נעילתו באופן ידני"</string>
- <string name="hidden_notifications_title" msgid="7139628534207443290">"קבל התראות מהר יותר"</string>
+ <string name="hidden_notifications_title" msgid="7139628534207443290">"קבל הודעות מהר יותר"</string>
<string name="hidden_notifications_text" msgid="2326409389088668981">"צפה בהן לפני שתבטל נעילה"</string>
<string name="hidden_notifications_cancel" msgid="3690709735122344913">"לא, תודה"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"הגדר"</string>
@@ -507,6 +507,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. הקש כדי להשתיק."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s פקדי עוצמת הקול גלויים. החלק כלפי מעלה כדי לסגור."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"פקדי עוצמת הקול מוסתרים"</string>
+ <string name="output_title" msgid="5355078100792942802">"פלט מדיה"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"פלט שיחת טלפון"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"לא נמצאו מכשירים"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"לא נמצאו מכשירים. יש לנסות להפעיל <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth ו-Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"הצג בשורת הסטטוס את אחוז עוצמת הסוללה"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"הצג את אחוז עוצמת הסוללה בתוך הסמל שבשורת הסטטוס כשהמכשיר אינו בטעינה"</string>
@@ -555,9 +562,9 @@
<string name="power_notification_controls_description" msgid="4372459941671353358">"בעזרת פקדים של הודעות הפעלה, תוכל להגדיר רמת חשיבות מ-0 עד 5 להודעות אפליקציה. \n\n"<b>"רמה 5"</b>" \n- הצג בראש רשימת ההודעות \n- אפשר הפרעה במסך מלא \n- תמיד אפשר הצצה \n\n"<b>"רמה 4"</b>" \n- מנע הפרעה במסך מלא \n- תמיד אפשר הצצה \n\n"<b>"רמה 3"</b>" \n- מנע הפרעה במסך מלא \n- אף פעם אל תאפשר הצצה \n\n"<b>"רמה 2"</b>" \n- מנע הפרעה במסך מלא \n- אף פעם אל תאפשר הצצה \n- אף פעם אל תאפשר קול ורטט \n\n"<b>"רמה 1"</b>" \n- מנע הפרעה במסך מלא \n- אף פעם אל תאפשר הצצה \n- אף פעם אל תאפשר קול ורטט \n- הסתר ממסך הנעילה ומשורת הסטטוס \n- הצג בתחתית רשימת ההודעות \n\n"<b>"רמה 0"</b>" \n- חסום את כל ההודעות מהאפליקציה"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"הודעות"</string>
<string name="notification_channel_disabled" msgid="2139193533791840539">"לא תקבל את ההודעות האלה יותר"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> קטגוריות של התראות"</string>
+ <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> קטגוריות של הודעות"</string>
<string name="notification_default_channel_desc" msgid="2506053815870808359">"האפליקציה הזו לא תומכת בקטגוריות של הודעות"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"לא ניתן לכבות התראות של האפליקציה הזאת"</string>
+ <string name="notification_unblockable_desc" msgid="3561016061737896906">"לא ניתן לכבות הודעות של האפליקציה הזאת"</string>
<plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
<item quantity="two">קטגוריית הודעות אחת מתוך <xliff:g id="NUMBER_1">%s</xliff:g> מאפליקציה זו</item>
<item quantity="many">קטגוריית הודעות אחת מתוך <xliff:g id="NUMBER_1">%s</xliff:g> מאפליקציה זו</item>
@@ -736,6 +743,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"הרחב"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"מזער"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"סגור"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"גרור למטה כדי לסגור"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"תפריט"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> במצב תמונה בתוך תמונה"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index f840225..983c014 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -33,7 +33,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"実行中"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string>
<string name="battery_low_title" msgid="6456385927409742437">"電池残量が少なくなっています"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"電池残量が少なくなっています。バッテリー セーバーを ON にしてください"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"残量が<xliff:g id="PERCENTAGE">%s</xliff:g>です"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"残量 <xliff:g id="PERCENTAGE">%s</xliff:g>、約 <xliff:g id="TIME">%s</xliff:g>(使用状況に基づく)"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"残量 <xliff:g id="PERCENTAGE">%s</xliff:g>、約 <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"残量が <xliff:g id="PERCENTAGE">%s</xliff:g> です。バッテリー セーバーは ON です。"</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB充電には対応していません。\n付属の充電器をお使いください。"</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"USB充電には対応していません。"</string>
@@ -67,14 +70,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"この端末に現在ログインしているユーザーでは、USB デバッグを ON にすることはできません。この機能を使用するには、メインユーザーに切り替えてください。"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"画面サイズに合わせて拡大"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"画面サイズに合わせて拡大"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"スクリーンショット"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"スクリーンショットを保存中..."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"スクリーンショットを保存しています..."</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"スクリーンショットを保存しています。"</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"スクリーンショットを取得しました。"</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"タップするとスクリーンショットが表示されます。"</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"スクリーンショットをキャプチャできませんでした。"</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"スクリーンショットの保存中に問題が発生しました。"</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"空き容量が足りないため、スクリーンショットを保存できません。"</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"スクリーンショットを保存しています"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"スクリーンショットを保存しました"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"タップするとスクリーンショットが表示されます"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"スクリーンショットを撮影できませんでした"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"スクリーンショットの保存中に問題が発生しました"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"空き容量が足りないため、スクリーンショットを保存できません"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"スクリーンショットの作成はアプリまたは組織で許可されていません"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USBファイル転送オプション"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"メディアプレーヤー(MTP)としてマウント"</string>
@@ -505,6 +509,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s。タップしてミュートします。"</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s の音量調節が表示されています。閉じるには、上にスワイプします。"</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"音量調節を非表示にしました"</string>
+ <string name="output_title" msgid="5355078100792942802">"メディア出力"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"通話の出力"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"デバイスが見つかりません"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"デバイスが見つかりません。<xliff:g id="SERVICE">%1$s</xliff:g> をオンにしてみてください"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth と Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"システムUI調整ツール"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"内蔵電池の残量の割合を表示する"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"充電していないときには電池残量の割合をステータスバーアイコンに表示する"</string>
@@ -552,26 +563,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"OFF"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"電源通知管理では、アプリの通知の重要度をレベル 0~5 で設定できます。\n\n"<b>"レベル 5"</b>" \n- 通知リストの一番上に表示する \n- 全画面表示を許可する \n- 常にポップアップする \n\n"<b>"レベル 4"</b>" \n- 全画面表示しない \n- 常にポップアップする \n\n"<b>"レベル 3"</b>" \n- 全画面表示しない \n- ポップアップしない \n\n"<b>"レベル 2"</b>" \n- 全画面表示しない \n- ポップアップしない \n- 音やバイブレーションを使用しない \n\n"<b>"レベル 1"</b>" \n- 全画面表示しない \n- ポップアップしない \n- 音やバイブレーションを使用しない \n- ロック画面やステータスバーに表示しない \n- 通知リストの一番下に表示する \n\n"<b>"レベル 0"</b>" \n- アプリからのすべての通知をブロックする"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"通知"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"今後、この通知は配信されません"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> 個の通知カテゴリ"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"このアプリでは通知カテゴリが設定されていません"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"このアプリの通知を OFF にすることはできません"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="other">このアプリの通知カテゴリ <xliff:g id="NUMBER_1">%s</xliff:g> 件中 1 件</item>
- <item quantity="one">このアプリの通知カテゴリ <xliff:g id="NUMBER_0">%s</xliff:g> 件中 1 件</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>、<xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>、<xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>、他 <xliff:g id="NUMBER_5">%3$d</xliff:g> 件</item>
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>、<xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>、他 <xliff:g id="NUMBER_2">%3$d</xliff:g> 件</item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"今後、この通知は表示されません"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"この通知を今後も表示しますか?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"通知を表示しない"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"今後も表示する"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"このアプリからの通知を今後も表示しますか?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"この通知を OFF にすることはできません"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> の通知管理は開いています"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> の通知管理は閉じています"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"このチャンネルからの通知を許可する"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"すべてのカテゴリ"</string>
<string name="notification_more_settings" msgid="816306283396553571">"詳細設定"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"カスタマイズ: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"カスタマイズ"</string>
<string name="notification_done" msgid="5279426047273930175">"完了"</string>
+ <string name="inline_undo" msgid="558916737624706010">"元に戻す"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"通知管理"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"通知スヌーズ設定"</string>
@@ -726,6 +730,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"展開"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"最小化"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"閉じる"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"設定"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"下にドラッグして閉じる"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"メニュー"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g>はピクチャー イン ピクチャーで表示中です"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 933a5e3..870c896 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -492,6 +492,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"ტონალური აკრეფა"</string>
<string name="stream_accessibility" msgid="301136219144385106">"მარტივი წვდომა"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"ზარები"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"დარეკვა"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"ვიბრაცია"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"დადუმება"</string>
@@ -502,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. შეეხეთ დასადუმებლად."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s-ის ხმის მართვის საშუალებები დამალულია. დასახურად, გადაფურცლეთ ზემოთ."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"ხმის მართვის საშუალებები დამალულია"</string>
+ <string name="output_title" msgid="5355078100792942802">"მედია გამომავალი"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"სატელეფონო ზარის გამომავალი სიგნალი"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"მოწყობილობები ვერ მოიძებნა"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"მოწყობილობები ვერ მოიძებნა. ცადეთ <xliff:g id="SERVICE">%1$s</xliff:g>-ის ჩართვა"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth და Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"სისტემის UI ტუნერი"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"ჩამაგრებული ბატარეის პროცენტის ჩვენება"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"ბატარეის დონის პროცენტის ჩვენება სტატუსის ზოლის ხატულას შიგნით, როდესაც არ იტენება"</string>
@@ -723,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"გაშლა"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"ჩაკეცვა"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"დახურვა"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"დასახურად ჩავლებით ჩამოიტანეთ ქვემოთ"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"მენიუ"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> იყენებს რეჟიმს „ეკრანი ეკრანში“"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index a9e6ffe..c4ae759 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Дыбысын өшіру үшін түртіңіз."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s дыбысты басқару элементтері көрсетулі. Сырғыту арқылы жабыңыз."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Дыбысты басқару элементтері жасырын"</string>
+ <string name="output_title" msgid="5355078100792942802">"Meдиа шығысы"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Телефон қоңырау шығысы"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Ешқандай құрылғы табылмады"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Ешқандай құрылғы табылмады. <xliff:g id="SERVICE">%1$s</xliff:g> қосып көріңіз"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth және Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Жүйелік пайдаланушылық интерфейс тюнері"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Ендірілген батарея пайыздық шамасын көрсету"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Зарядталмай тұрғанда, күй жолағы белгішесінің ішінде батарея деңгейінің пайыздық шамасын көрсетеді"</string>
@@ -724,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Жаю"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Кішірейту"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Жабу"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Жабу үшін төмен қарай сүйреңіз"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Mәзір"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> \"сурет ішіндегі сурет\" режимінде"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 5819f2a..12733f1 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s ។ ចុចដើម្បីបិទសំឡេង។"</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"អង្គគ្រប់គ្រងកម្រិតសំឡេង %s បានបង្ហាញ។ អូសឡើងលើដើម្បីបដិសេធ។"</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"អង្គគ្រប់គ្រងកម្រិតសំឡេងបានលាក់"</string>
+ <string name="output_title" msgid="5355078100792942802">"លទ្ធផលមេឌៀ"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"លទ្ធផលនៃការហៅទូរសព្ទ"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"រកមិនឃើញឧបករណ៍ទេ"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"រកមិនឃើញឧបករណ៍ទេ។ សាកល្បងបើក <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"ប៊្លូធូស"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"ប៊្លូធូស និង Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"កម្មវិធីសម្រួល UI ប្រព័ន្ធ"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"បង្ហាញភាគរយថាមពលថ្មដែលបានបង្កប់"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"បង្ហាញភាគរយនៃកម្រិតថាមពលថ្មនៅក្នុងរូបតំណាងរបារស្ថានភាពនៅពេលមិនសាកថ្ម"</string>
@@ -724,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"ពង្រីក"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"បង្រួម"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"បិទ"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"អូសចុះក្រោមដើម្បីបដិសេធ"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"ម៉ឺនុយ"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> ស្ថិតក្នុងមុខងាររូបក្នុងរូប"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 5e7e1e2..3ce5e76 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -33,7 +33,13 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"ಚಾಲ್ತಿಯಲ್ಲಿರುವ"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"ಅಧಿಸೂಚನೆಗಳು"</string>
<string name="battery_low_title" msgid="6456385927409742437">"ಬ್ಯಾಟರಿ ಕಡಿಮೆ ಇದೆ"</string>
+ <!-- no translation found for battery_low_title_hybrid (6268991275887381595) -->
+ <skip />
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> ಉಳಿದಿದೆ"</string>
+ <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) -->
+ <skip />
+ <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) -->
+ <skip />
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> ಉಳಿದಿದೆ. ಬ್ಯಾಟರಿ ಉಳಿತಾಯ ಆನ್ ಆಗಿದೆ."</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB ಚಾರ್ಜಿಂಗ್ ಬೆಂಬಲಿತವಾಗಿಲ್ಲ.\nಒದಗಿಸಿರುವ ಚಾರ್ಜರ್ ಮಾತ್ರ ಬಳಸಿ."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"USB ಚಾರ್ಜಿಂಗ್ ಬೆಂಬಲಿತವಾಗಿಲ್ಲ."</string>
@@ -67,14 +73,22 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"ಬಳಕೆದಾರರು ಪ್ರಸ್ತುತ ಈ ಸಾಧನಕ್ಕೆ ಸೈನ್ ಇನ್ ಮಾಡಿದ್ದಾರೆ USB ಡೀಬಗ್ ಮಾಡುವುದನ್ನು ಆನ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ. ಈ ವೈಶಿಷ್ಟ್ಯವನ್ನು ಬಳಸಲು, ಪ್ರಾಥಮಿಕ ಬಳಕೆದಾರರಿಗೆ ಬದಲಾಯಿಸಿ."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"ಪರದೆ ತುಂಬಿಸಲು ಝೂಮ್ ಮಾಡು"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"ಪರದೆ ತುಂಬಿಸಲು ವಿಸ್ತಾರಗೊಳಿಸು"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಉಳಿಸಲಾಗುತ್ತಿದೆ…"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಉಳಿಸಲಾಗುತ್ತಿದೆ…"</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಉಳಿಸಲಾಗುತ್ತಿದೆ."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಸೆರೆಹಿಡಿಯಲಾಗಿದೆ."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ಶಾಟ್ ವೀಕ್ಷಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಸೆರೆಹಿಡಿಯಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಉಳಿಸುವಲ್ಲಿ ಸಮಸ್ಯೆ ಎದುರಾಗಿದೆ."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"ಪರಿಮಿತ ಸಂಗ್ರಹಣೆ ಸ್ಥಳದ ಕಾರಣದಿಂದಾಗಿ ಸ್ಕ್ರೀನ್ಶಾಟ್ ಉಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
+ <!-- no translation found for screenshot_saving_text (2545047868936087248) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_title (5637073968117370753) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_text (7574667448002050363) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_title (9096484883063264803) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) -->
+ <skip />
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"ಅಪ್ಲಿಕೇಶನ್ ಅಥವಾ ಸಂಸ್ಥೆಯು ಸ್ಕ್ರೀನ್ಶಾಟ್ಗಳನ್ನು ತೆಗೆಯುವುದನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB ಫೈಲ್ ವರ್ಗಾವಣೆ ಆಯ್ಕೆಗಳು"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"ಮೀಡಿಯಾ ಪ್ಲೇಯರ್ ರೂಪದಲ್ಲಿ ಅಳವಡಿಸಿ (MTP)"</string>
@@ -492,8 +506,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"ಬ್ಲೂಟೂತ್"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"ಡ್ಯುಯಲ್ ಬಹು ಟೋನ್ ಆವರ್ತನೆ"</string>
<string name="stream_accessibility" msgid="301136219144385106">"ಪ್ರವೇಶಿಸುವಿಕೆ"</string>
- <!-- no translation found for ring_toggle_title (3281244519428819576) -->
- <skip />
+ <string name="ring_toggle_title" msgid="3281244519428819576">"ಕರೆಗಳು"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"ರಿಂಗ್"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"ವೈಬ್ರೇಟ್"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"ಮ್ಯೂಟ್"</string>
@@ -504,6 +517,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. ಮ್ಯೂಟ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s ವಾಲ್ಯೂಮ್ ನಿಯಂತ್ರಣಗಳನ್ನು ತೋರಿಸಲಾಗಿದೆ. ವಜಾಗೊಳಿಸಲು ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"ವಾಲ್ಯೂಮ್ ನಿಯಂತ್ರಣಗಳನ್ನು ಮರೆಮಾಡಲಾಗಿದೆ"</string>
+ <string name="output_title" msgid="5355078100792942802">"ಮೀಡಿಯಾ ಔಟ್ಪುಟ್"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"ಫೋನ್ ಕರೆ ಔಟ್ಪುಟ್"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"ಯಾವ ಸಾಧನಗಳೂ ಕಂಡುಬಂದಿಲ್ಲ"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"ಯಾವುದೇ ಸಾಧನಗಳು ಪತ್ತೆಯಾಗಿಲ್ಲ. <xliff:g id="SERVICE">%1$s</xliff:g> ಅನ್ನು ಆನ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿ"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"ಬ್ಲೂಟೂತ್"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"ವೈ-ಫೈ"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"ಬ್ಲೂಟೂತ್ ಮತ್ತು ವೈ-ಫೈ"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"ಸಿಸ್ಟಂ UI ಟ್ಯೂನರ್"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"ಎಂಬೆಡ್ ಮಾಡಲಾದ ಬ್ಯಾಟರಿ ಶೇಕಡಾ ತೋರಿಸಿ"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"ಚಾರ್ಜ್ ಮಾಡದಿರುವಾಗ ಸ್ಥಿತಿ ಪಟ್ಟಿ ಐಕಾನ್ ಒಳಗೆ ಬ್ಯಾಟರಿ ಮಟ್ಟದ ಶೇಕಡಾವನ್ನು ತೋರಿಸಿ"</string>
@@ -551,26 +571,27 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ಆಫ್"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"ಪವರ್ ಅಧಿಸೂಚನೆ ನಿಯಂತ್ರಣಗಳ ಮೂಲಕ, ನೀವು ಅಪ್ಲಿಕೇಶನ್ಗಳ ಅಧಿಸೂಚನೆಗಳನ್ನು 0 ರಿಂದ 5 ರವರೆಗಿನ ಹಂತಗಳ ಪ್ರಾಮುಖ್ಯತೆಯನ್ನು ಹೊಂದಿಸಬಹುದು. \n\n"<b>"ಹಂತ 5"</b>" \n- ಮೇಲಿನ ಅಧಿಸೂಚನೆ ಪಟ್ಟಿಯನ್ನು ತೋರಿಸಿ \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ಅನುಮತಿಸಿ \n- ಯಾವಾಗಲು ಇಣುಕು ನೋಟ \n\n"<b>"ಹಂತ 4"</b>" \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ತಡೆಯಿರಿ \n- ಯಾವಾಗಲು ಇಣುಕು ನೋಟ\n\n"<b>"ಹಂತ 3"</b>" \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ತಡೆಯಿರಿ \n- ಎಂದಿಗೂ ಇಣುಕು ನೋಟ ಬೇಡ \n\n"<b>"ಹಂತ 2"</b>" \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ತಡೆಯಿರಿ \n- ಎಂದಿಗೂ ಇಣುಕು ನೋಟ ಬೇಡ \n- ಶಬ್ದ ಮತ್ತು ವೈಬ್ರೇಷನ್ ಎಂದಿಗೂ ಮಾಡಬೇಡಿ \n\n"<b>"ಹಂತ 1"</b>" \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ತಡೆಯಿರಿ \n- ಎಂದಿಗೂ ಇಣುಕು ನೋಟ ಬೇಡ \n- ಶಬ್ದ ಮತ್ತು ವೈಬ್ರೇಷನ್ ಎಂದಿಗೂ ಮಾಡಬೇಡಿ \n- ಸ್ಥಿತಿ ಪಟ್ಟಿ ಮತ್ತು ಲಾಕ್ ಪರದೆಯಿಂದ ಮರೆಮಾಡಿ \n- ಕೆಳಗಿನ ಅಧಿಸೂಚನೆ ಪಟ್ಟಿಯನ್ನು ತೋರಿಸಿ \n\n"<b>"ಹಂತ 0"</b>" \n- ಅಪ್ಲಿಕೇಶನ್ನಿಂದ ಎಲ್ಲಾ ಅಧಿಸೂಚನೆಗಳನ್ನು ನಿರ್ಬಂಧಿಸಿ"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"ಅಧಿಸೂಚನೆಗಳು"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"ನೀವು ಇನ್ನು ಮುಂದೆ ಈ ಅಧಿಸೂಚನೆಗಳನ್ನು ಪಡೆಯುವುದಿಲ್ಲ"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> ಅಧಿಸೂಚನೆ ವರ್ಗಗಳು"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಅಧಿಸೂಚನೆ ವರ್ಗಗಳನ್ನು ಹೊಂದಿಲ್ಲ"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"ಈ ಅಪ್ಲಿಕೇಶನ್ನಿಂದ ಅಧಿಸೂಚನೆಗಳನ್ನು ಆಫ್ ಮಾಡಲಾಗುವುದಿಲ್ಲ"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="one">ಈ ಅಪ್ಲಿಕೇಶನ್ನಿಂದ <xliff:g id="NUMBER_1">%s</xliff:g> ಅಧಿಸೂಚನೆ ವರ್ಗಗಳಲ್ಲಿ 1 ವರ್ಗ</item>
- <item quantity="other">ಈ ಅಪ್ಲಿಕೇಶನ್ನಿಂದ <xliff:g id="NUMBER_1">%s</xliff:g> ಅಧಿಸೂಚನೆ ವರ್ಗಗಳಲ್ಲಿ 1 ವರ್ಗ</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, ಮತ್ತು ಇತರ <xliff:g id="NUMBER_5">%3$d</xliff:g></item>
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, ಮತ್ತು ಇತರ <xliff:g id="NUMBER_5">%3$d</xliff:g></item>
- </plurals>
+ <!-- no translation found for notification_channel_disabled (344536703863700565) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing (8945102997083836858) -->
+ <skip />
+ <!-- no translation found for inline_stop_button (4172980096860941033) -->
+ <skip />
+ <!-- no translation found for inline_keep_button (6665940297019018232) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing_app (1723113469580031041) -->
+ <skip />
+ <!-- no translation found for notification_unblockable_desc (1037434112919403708) -->
+ <skip />
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನ ಅಧಿಸೂಚನೆ ನಿಯಂತ್ರಣಗಳನ್ನು ತೆರೆಯಲಾಗಿದೆ"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನ ಅಧಿಸೂಚನೆ ನಿಯಂತ್ರಣಗಳನ್ನು ಮುಚ್ಚಲಾಗಿದೆ"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"ಈ ಚಾನಲ್ನ ಅಧಿಸೂಚನೆಗಳಿಗೆ ಅನುಮತಿ ನೀಡಿ"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"ಎಲ್ಲ ವರ್ಗಗಳು"</string>
<string name="notification_more_settings" msgid="816306283396553571">"ಹೆಚ್ಚಿನ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"ಕಸ್ಟಮೈಸ್: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <!-- no translation found for notification_app_settings (420348114670768449) -->
+ <skip />
<string name="notification_done" msgid="5279426047273930175">"ಮುಗಿದಿದೆ"</string>
+ <!-- no translation found for inline_undo (558916737624706010) -->
+ <skip />
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"ಅಧಿಸೂಚನೆ ನಿಯಂತ್ರಣಗಳು"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"ಅಧಿಸೂಚನೆ ಸ್ನೂಜ್ ಆಯ್ಕೆಗಳು"</string>
@@ -725,6 +746,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"ವಿಸ್ತೃತಗೊಳಿಸು"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"ಕುಗ್ಗಿಸಿ"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"ಮುಚ್ಚಿ"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"ವಜಾಗೊಳಿಸಲು ಕೆಳಕ್ಕೆ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"ಮೆನು"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> ಚಿತ್ರದಲ್ಲಿನ ಚಿತ್ರದಲ್ಲಿದೆ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index d86c32f..7fa87ed 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -505,6 +505,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. 탭하여 음소거로 설정하세요."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s 볼륨 컨트롤이 표시됩니다. 닫으려면 위로 스와이프합니다."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"볼륨 컨트롤 숨김"</string>
+ <string name="output_title" msgid="5355078100792942802">"미디어 출력"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"전화 통화 출력"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"기기를 찾을 수 없음"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"기기를 찾을 수 없습니다. <xliff:g id="SERVICE">%1$s</xliff:g>을(를) 사용 설정해 보세요"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"블루투스"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"블루투스 및 Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"시스템 UI 튜너"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"내장형 배터리 잔량 비율 표시"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"충전 중이 아닌 경우 상태 표시줄 아이콘 내에 배터리 잔량 비율 표시"</string>
@@ -726,6 +733,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"펼치기"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"최소화"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"닫기"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"아래로 드래그하여 닫기"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"메뉴"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g>에서 PIP 사용 중"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index e10e2b0..e3f1fa3 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Үнүн өчүрүү үчүн басыңыз."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s үндү башкаруу элементтери көрсөтүлгөн. Этибарга албоо үчүн өйдө серпип коюңуз."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Үндү башкаруу элементтери жашырылган"</string>
+ <string name="output_title" msgid="5355078100792942802">"Медиа түзмөк"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Телефон чалуу"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Түзмөктөр табылган жок"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Түзмөктөр табылган жок. <xliff:g id="SERVICE">%1$s</xliff:g> кызматын күйгүзүп көрүңүз"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi‑Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth жана Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Батарянын кубатнын деңгээли пайыз менен көрсөтлсүн"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Түзмөк кубаттанбай турганда, батареянын деңгээли статус тилкесинде көрүнүп турат"</string>
@@ -724,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Жайып көрсөтүү"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Кичирейтүү"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Жабуу"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Четке кагуу үчүн төмөн сүйрөңүз"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Меню"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> – сүрөт ичиндеги сүрөт"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 2587a20..fe262ee 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. ແຕະເພື່ອປິດສຽງ."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"ສະແດງສ່ວນຄວບຄຸມສຽງ %s ແລ້ວ. ປັດອອກຂ້າງເພື່ອປິດໄວ້."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"ເຊື່ອງສ່ວນຄວບຄຸມສຽງແລ້ວ"</string>
+ <string name="output_title" msgid="5355078100792942802">"ມີເດຍເອົ້າພຸດ"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"ເອົ້າພຸດສາຍໂທອອກ"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"ບໍ່ພົບອຸປະກອນ"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"ບໍ່ພົບອຸປະກອນ. ກະລຸນາລອງເປີດ <xliff:g id="SERVICE">%1$s</xliff:g> ກ່ອນ"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth ແລະ Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"ສະແດງເປີເຊັນແບັດເຕີຣີທີ່ຕິດມາ"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"ສະແດງເປີເຊັນລະດັບແບັດເຕີຣີຢູ່ດ້ານໃນໄອຄອນແຖບສະຖານະ ເມື່ອບໍ່ສາກຢູ່"</string>
@@ -724,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"ຂະຫຍາຍ"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"ຫຍໍ້"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"ປິດ"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"ລາກລົງເພື່ອປິດໄວ້"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"ເມນູ"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> ແມ່ນເປັນການສະແດງຜົນຫຼາຍຢ່າງພ້ອມກັນ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 1c97452..a613b23 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -35,7 +35,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Vykstantys"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Pranešimai"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Akumuliatorius senka"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Akumuliatorius senka. Įjunkite Akumuliatoriaus tausojimo priemonę"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"Liko <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Liko: <xliff:g id="PERCENTAGE">%s</xliff:g> (atsižvelgiant į naudojimą liko maždaug <xliff:g id="TIME">%s</xliff:g>)"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Liko: <xliff:g id="PERCENTAGE">%s</xliff:g> (liko maždaug <xliff:g id="TIME">%s</xliff:g>)"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Liko <xliff:g id="PERCENTAGE">%s</xliff:g>. Akumuliatoriaus tausojimo priemonė įjungta."</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB krovimas nepalaikomas.\nNaudokite tik pateiktą įkroviklį."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"USB įkrovimas nepalaikomas."</string>
@@ -69,14 +72,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Šiuo metu prie įrenginio prisijungęs naudotojas negali įjungti USB derinimo. Kad galėtumėte naudoti šią funkciją, perjunkite į pagrindinį naudotoją."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Keisti mast., kad atit. ekr."</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Ištempti, kad atit. ekr."</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Ekrano kopija"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Išsaugoma ekrano kopija..."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Išsaugoma ekrano kopija..."</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Išsaugoma ekrano kopija."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Ekrano kopija užfiksuota."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Palieskite, kad peržiūrėtumėte ekrano kopiją."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Nepavyko užfiksuoti ekrano kopijos."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Išsaugant ekrano kopiją iškilo problemų."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Negalima išsaugoti ekrano kopijos dėl ribotos saugyklos vietos."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"Ekrano kopija išsaugoma"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"Ekrano kopija išsaugota"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Palieskite, kad peržiūrėtumėte ekrano kopiją"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"Nepavyko užfiksuoti ekrano kopijos"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Išsaugant ekrano kopiją kilo problema"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Negalima išsaugoti ekrano kopijos dėl ribotos saugyklos vietos"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Jūsų organizacijoje arba naudojant šią programą neleidžiama daryti ekrano kopijų"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB failo perdavimo parinktys"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Įmontuoti kaip medijos leistuvę (MTP)"</string>
@@ -507,6 +511,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Palieskite, kad nutildytumėte."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Rodomi „%s“ garsumo valdikliai. Perbraukite į viršų, kad atsisakytumėte."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Garsumo valdikliai paslėpti"</string>
+ <string name="output_title" msgid="5355078100792942802">"Medijos išvestis"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Telefono skambučių išvestis"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Įrenginių nerasta"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Įrenginių nerasta. Bandykite įjungti „<xliff:g id="SERVICE">%1$s</xliff:g>“"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"„Bluetooth“ ir „Wi-Fi“"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Sistemos naudotojo sąsajos derinimo priemonė"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Rodyti įterptą akumuliat. įkrovos procentinę vertę"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Rodyti akumuliatoriaus įkrovos lygio procentinę vertę būsenos juostos piktogramoje, kai įrenginys nėra įkraunamas"</string>
@@ -554,30 +565,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Išjungta"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"Naudodami pranešimų valdiklius galite nustatyti programos pranešimų svarbos lygį nuo 0 iki 5. \n\n"<b>"5 lygis"</b>" \n– Rodyti pranešimų sąrašo viršuje \n– Leisti pertraukti, kai veikia viso ekrano režimas \n– Visada rodyti pranešimus \n\n"<b>"4 lygis"</b>" \n– Neleisti pertraukti viso ekrano režimo \n– Visada rodyti pranešimus \n\n"<b>"3 lygis"</b>" \n– Neleisti pertraukti viso ekrano režimo \n– Niekada nerodyti pranešimų \n\n"<b>"2 lygis"</b>" \n– Neleisti pertraukti viso ekrano režimo \n– Niekada nerodyti pranešimų \n– Niekada neleisti garso ir nevibruoti \n\n"<b>"1 lygis"</b>" \n– Neleisti pertraukti viso ekrano režimo \n– Niekada nerodyti pranešimų \n– Niekada neleisti garso ir nevibruoti \n– Slėpti užrakinimo ekrane ir būsenos juostoje \n– Rodyti pranešimų sąrašo apačioje \n\n"<b>"0 lygis"</b>" \n– Blokuoti visus programos pranešimus"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Pranešimai"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"Nebegausite šių pranešimų"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"Pranešimų kategorijų: <xliff:g id="NUMBER">%d</xliff:g>"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"Šioje programoje nėra pranešimų kategorijų"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Šios programos pranešimų negalima išjungti"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="one">1 iš <xliff:g id="NUMBER_1">%s</xliff:g> šios programos pranešimų kategorijos</item>
- <item quantity="few">1 iš <xliff:g id="NUMBER_1">%s</xliff:g> šios programos pranešimų kategorijų</item>
- <item quantity="many">1 iš <xliff:g id="NUMBER_1">%s</xliff:g> šios programos pranešimų kategorijos</item>
- <item quantity="other">1 iš <xliff:g id="NUMBER_1">%s</xliff:g> šios programos pranešimų kategorijų</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"„<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>“, „<xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>“"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="one">„<xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>“, „<xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>“ ir dar <xliff:g id="NUMBER_5">%3$d</xliff:g> kanalas</item>
- <item quantity="few">„<xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>“, „<xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>“ ir dar <xliff:g id="NUMBER_5">%3$d</xliff:g> kanalai</item>
- <item quantity="many">„<xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>“, „<xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>“ ir dar <xliff:g id="NUMBER_5">%3$d</xliff:g> kanalo</item>
- <item quantity="other">„<xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>“, „<xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>“ ir dar <xliff:g id="NUMBER_5">%3$d</xliff:g> kanalų</item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"Nebematysite šių pranešimų"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"Toliau rodyti šiuos pranešimus?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"Sustabdyti pranešimus"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Toliau rodyti"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"Toliau rodyti iš šios programos gautus pranešimus?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"Šių pranešimų negalima išjungti"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ pranešimų valdikliai atidaryti"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ pranešimų valdikliai uždaryti"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Leisti pranešimus iš šio kanalo"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"Visos kategorijos"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Daugiau nustatymų"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Tinkinti: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"Tinkinti"</string>
<string name="notification_done" msgid="5279426047273930175">"Atlikta"</string>
+ <string name="inline_undo" msgid="558916737624706010">"Anuliuoti"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"pranešimų valdikliai"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"pranešimų snaudimo parinktys"</string>
@@ -736,6 +736,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Išskleisti"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Sumažinti"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Uždaryti"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Nustatymai"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Nuvilkite žemyn, kad atsisakytumėte"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Meniu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> rodom. vaizdo vaizde"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index c14eb3f..574d898 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -505,6 +505,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Pieskarieties, lai izslēgtu skaņu."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Tiek rādītas %s skaļuma vadīklas. Velciet augšup, lai nerādītu."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Skaļuma vadīklas paslēptas"</string>
+ <string name="output_title" msgid="5355078100792942802">"Multivides izvade"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Tālruņa zvana izvade"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Nav atrasta neviena ierīce"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Nav atrasta neviena ierīce. Ieslēdziet <xliff:g id="SERVICE">%1$s</xliff:g>."</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth un Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Sistēmas saskarnes regulators"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Rādīt akumulatora uzlādes līmeni procentos"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Rādīt akumulatora uzlādes līmeni procentos statusa joslas ikonā, kad netiek veikta uzlāde"</string>
@@ -730,6 +737,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Izvērst"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimizēt"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Aizvērt"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Velciet lejup, lai noraidītu"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Izvēlne"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> ir attēlā attēlā"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index e30085c..b64217d 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Допрете за да се исклучи звукот."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Прикажани се контролите за јачина на звукот на %s. Повлечете нагоре за да отфрлите."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Скриени се контролите за јачина на звукот"</string>
+ <string name="output_title" msgid="5355078100792942802">"Излез за аудиовизуелни содржини"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Излез за телефонски повик"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Не се најдени уреди"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Не се најдени уреди. Обидете се да вклучите <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth и Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Адаптер на УИ на системот"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Прикажи вграден процент на батеријата"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Прикажи процент на ниво на батеријата во внатрешноста на иконата со статусна лента кога не се полни"</string>
@@ -724,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Проширете"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Минимизирај"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Затвори"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Повлечете надолу за да отфрлите"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Мени"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> е во слика во слика"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 385331e..f3fa9e9 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -33,7 +33,13 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"നടന്നുകൊണ്ടിരിക്കുന്നവ"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"അറിയിപ്പുകൾ"</string>
<string name="battery_low_title" msgid="6456385927409742437">"ബാറ്ററി കുറവാണ്"</string>
+ <!-- no translation found for battery_low_title_hybrid (6268991275887381595) -->
+ <skip />
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> ശേഷിക്കുന്നു"</string>
+ <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) -->
+ <skip />
+ <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) -->
+ <skip />
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> ശേഷിക്കുന്നു. ബാറ്ററി ലാഭിക്കൽ ഓണാണ്."</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB ചാർജ്ജുചെയ്യൽ പിന്തുണയ്ക്കുന്നില്ല.\nഅതിന്റെ അനുബന്ധ ചാർജ്ജർ മാത്രം ഉപയോഗിക്കുക."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"USB ചാർജ്ജുചെയ്യൽ പിന്തുണച്ചില്ല."</string>
@@ -67,14 +73,22 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"ഉപകരണത്തിൽ ഇപ്പോൾ സൈൻ ഇൻ ചെയ്തിരിക്കുന്ന ഉപയോക്താവിന് USB ഡീബഗ്ഗിംഗ് ഓണാക്കാനാകില്ല. ഈ ഫീച്ചർ ഉപയോഗിക്കാൻ പ്രാഥമിക ഉപയോക്താവിലേക്ക് മാറുക."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"സ്ക്രീനിൽ ഉൾക്കൊള്ളിക്കാൻ സൂം ചെയ്യുക"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"സ്ക്രീനിൽ ഉൾക്കൊള്ളിക്കാൻ വലിച്ചുനീട്ടുക"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"സ്ക്രീൻഷോട്ട് സംരക്ഷിക്കുന്നു..."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"സ്ക്രീൻഷോട്ട് സംരക്ഷിക്കുന്നു..."</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"സ്ക്രീൻഷോട്ട് സംരക്ഷിക്കുന്നു."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"സ്ക്രീൻഷോട്ട് എടുത്തു."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"നിങ്ങളുടെ സ്ക്രീൻഷോട്ട് കാണുന്നതിന് ടാപ്പുചെയ്യുക."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"സ്ക്രീൻഷോട്ട് എടുക്കാൻ കഴിഞ്ഞില്ല."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"സ്ക്രീൻഷോട്ട് സംരക്ഷിക്കുന്ന സമയത്ത് പ്രശ്നം നേരിട്ടു."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"സ്റ്റോറേജ് ഇടം പരിമിതമായതിനാൽ സ്ക്രീൻഷോട്ട് സംരക്ഷിക്കാൻ കഴിയില്ല."</string>
+ <!-- no translation found for screenshot_saving_text (2545047868936087248) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_title (5637073968117370753) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_text (7574667448002050363) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_title (9096484883063264803) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) -->
+ <skip />
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"സ്ക്രീൻഷോട്ടുകൾ എടുക്കുന്നത് ആപ്പോ നിങ്ങളുടെ സ്ഥാപനമോ അനുവദിക്കുന്നില്ല"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB ഫയൽ കൈമാറൽ ഓപ്ഷനുകൾ"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"ഒരു മീഡിയ പ്ലേയറായി (MTP) മൗണ്ടുചെയ്യുക"</string>
@@ -492,8 +506,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"ബ്ലൂടൂത്ത്"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"ഡ്യുവൽ മൾട്ടി റ്റോൺ ഫ്രീക്വൻസി"</string>
<string name="stream_accessibility" msgid="301136219144385106">"ഉപയോഗസഹായി"</string>
- <!-- no translation found for ring_toggle_title (3281244519428819576) -->
- <skip />
+ <string name="ring_toggle_title" msgid="3281244519428819576">"കോളുകൾ"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"റിംഗ് ചെയ്യുക"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"വൈബ്രേറ്റ് ചെയ്യുക"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"മ്യൂട്ട് ചെയ്യുക"</string>
@@ -504,6 +517,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s മ്യൂട്ടുചെയ്യുന്നതിന് ടാപ്പുചെയ്യുക."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s വോളിയം നിയന്ത്രണങ്ങൾ കാണിച്ചിരിക്കുന്നു. ഡിസ്മിസ് ചെയ്യുന്നതിന് മുകളിലേക്ക് സ്വൈപ്പുചെയ്യുക."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"വോളിയം നിയന്ത്രണങ്ങൾ മറച്ചിരിക്കുന്നു"</string>
+ <string name="output_title" msgid="5355078100792942802">"മീഡിയ ഔട്ട്പുട്ട്"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"ഫോൺ കോൾ ഔട്ട്പുട്ട്"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"ഉപകരണങ്ങളൊന്നും കണ്ടെത്തിയില്ല"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"ഉപകരണങ്ങളൊന്നും കണ്ടെത്തിയില്ല. <xliff:g id="SERVICE">%1$s</xliff:g> ഓണാക്കുന്നത് പരീക്ഷിക്കുക"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"വൈഫൈ"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth-ഉം വൈഫൈയും"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"സിസ്റ്റം UI ട്യൂണർ"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"എംബഡ് ചെയ്ത ബാറ്ററി ശതമാനം കാണിക്കുക"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"ചാർജ്ജുചെയ്യാതിരിക്കുമ്പോൾ സ്റ്റാറ്റസ് ബാർ ഐക്കണിൽ ബാറ്ററി ലെവൽ ശതമാനം കാണിക്കുക"</string>
@@ -551,26 +571,27 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ഓഫ്"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"പവർ അറിയിപ്പ് നിയന്ത്രണം ഉപയോഗിച്ച്, ഒരു ആപ്പിനായുള്ള അറിയിപ്പുകൾക്ക് 0 മുതൽ 5 വരെയുള്ള പ്രാധാന്യ ലെവലുകളിലൊന്ന് നിങ്ങൾക്ക് സജ്ജമാക്കാവുന്നതാണ്. \n\n"<b>"ലെവൽ 5"</b>" \n- അറിയിപ്പ് ലിസ്റ്റിന്റെ മുകളിൽ കാണിക്കുക \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം അനുവദിക്കുക \n- എല്ലായ്പ്പോഴും ദൃശ്യമാക്കുക \n\n"<b>"ലെവൽ 4"</b>" \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം തടയുക \n- എല്ലായ്പ്പോഴും ദൃശ്യമാക്കുക \n\n"<b>"ലെവൽ 3"</b>" \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം തടയുക \n- ഒരിക്കലും സൃശ്യമാക്കരുത് \n\n"<b>"ലെവൽ 2"</b>" \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം തടയുക \n- ഒരിക്കലും ദൃശ്യമാക്കരുത് \n- ഒരിക്കലും ശബ്ദവും വൈബ്രേഷനും ഉണ്ടാക്കരുത് \n\n"<b>"ലെവൽ 1"</b>" \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം തടയുക \n- ഒരിക്കലും ദൃശ്യമാക്കരുത് \n- ഒരിക്കലും ശബ്ദവും വൈബ്രേഷനും ഉണ്ടാക്കരുത് \n- ലോക്ക് സ്ക്രീനിൽ നിന്നും സ്റ്റാറ്റസ് ബാറിൽ നിന്നും മറയ്ക്കുക \n- അറിയിപ്പ് ലിസ്റ്റിന്റെ അടിയിൽ കാണിക്കുക \n\n"<b>"ലെവൽ 0"</b>" \n- ആപ്പിൽ നിന്നുള്ള എല്ലാ അറിയിപ്പുകളും ബ്ലോക്കുചെയ്യുക"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"അറിയിപ്പുകൾ"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"നിങ്ങൾക്ക് ഇനി ഈ അറിയിപ്പുകൾ ലഭിക്കില്ല"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> അറിയിപ്പ് വിഭാഗങ്ങൾ"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"ഈ ആപ്പിന് അറിയിപ്പ് വിഭാഗങ്ങളില്ല"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"ഈ ആപ്പിൽ നിന്നുള്ള അറിയിപ്പുകൾ ഓഫാക്കാൻ കഴിയില്ല"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="other">ഈ ആപ്പിൽ നിന്ന് 1 / <xliff:g id="NUMBER_1">%s</xliff:g> അറിയിപ്പ് വിഭാഗങ്ങൾ</item>
- <item quantity="one">ഈ ആപ്പിൽ നിന്ന് 1 / <xliff:g id="NUMBER_0">%s</xliff:g> അറിയിപ്പ് വിഭാഗം</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> എന്നിവയും മറ്റ് <xliff:g id="NUMBER_5">%3$d</xliff:g> എണ്ണവും</item>
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> എന്നിവയും മറ്റ് <xliff:g id="NUMBER_2">%3$d</xliff:g> എണ്ണവും</item>
- </plurals>
+ <!-- no translation found for notification_channel_disabled (344536703863700565) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing (8945102997083836858) -->
+ <skip />
+ <!-- no translation found for inline_stop_button (4172980096860941033) -->
+ <skip />
+ <!-- no translation found for inline_keep_button (6665940297019018232) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing_app (1723113469580031041) -->
+ <skip />
+ <!-- no translation found for notification_unblockable_desc (1037434112919403708) -->
+ <skip />
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> ആപ്പിന്റെ അറിയിപ്പ് നിയന്ത്രണങ്ങൾ തുറന്നു"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> ആപ്പിന്റെ അറിയിപ്പ് നിയന്ത്രണങ്ങൾ അടച്ചു"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"ഈ ചാനലിൽ നിന്നുള്ള അറിയിപ്പുകൾ അനുവദിക്കുക"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"എല്ലാ വിഭാഗങ്ങളും"</string>
<string name="notification_more_settings" msgid="816306283396553571">"കൂടുതൽ ക്രമീകരണം"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"ഇഷ്ടാനുസൃതമാക്കുക: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <!-- no translation found for notification_app_settings (420348114670768449) -->
+ <skip />
<string name="notification_done" msgid="5279426047273930175">"പൂർത്തിയായി"</string>
+ <!-- no translation found for inline_undo (558916737624706010) -->
+ <skip />
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"അറിയിപ്പ് നിയന്ത്രണങ്ങൾ"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"അറിയിപ്പ് സ്നൂസ് ഓപ്ഷനുകൾ"</string>
@@ -725,6 +746,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"വികസിപ്പിക്കുക"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"ചെറുതാക്കുക"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"അവസാനിപ്പിക്കുക"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"ക്രമീകരണം"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"തള്ളിക്കളയാൻ താഴേക്ക് വലിച്ചിടുക"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"മെനു"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> ചിത്രത്തിനുള്ളിലെ ചിത്രത്തിലാണ്"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 480956c..42bd38b 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -503,6 +503,13 @@
<!-- no translation found for volume_dialog_accessibility_shown_message (1834631467074259998) -->
<skip />
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Түвшний удирдлагыг нуусан"</string>
+ <string name="output_title" msgid="5355078100792942802">"Медиа гаралт"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Утасны дуудлагын гаралт"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Төхөөрөмж олдсонгүй"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Төхөөрөмж олдсонгүй. <xliff:g id="SERVICE">%1$s</xliff:g>-г асааж үзнэ үү"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth болон Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Системийн UI Тохируулагч"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Залгаатай тэжээлийн хувийг харуулах"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Тэжээлийн хувийг цэнэглээгүй байх үед статусын хэсэгт харуулна уу"</string>
@@ -724,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Дэлгэх"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Багасгах"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Хаах"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Хаахын тулд доош чирэх"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Цэс"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> дэлгэцэн доторх дэлгэцэд байна"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 8a0d661..803366f 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -33,7 +33,13 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"सुरु असलेले"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"सूचना"</string>
<string name="battery_low_title" msgid="6456385927409742437">"बॅटरी कमी आहे"</string>
+ <!-- no translation found for battery_low_title_hybrid (6268991275887381595) -->
+ <skip />
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> शिल्लक"</string>
+ <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) -->
+ <skip />
+ <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) -->
+ <skip />
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> शिल्लक. बॅटरी सेव्हर चालू आहे."</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB चार्जिंग समर्थित नाही.\nफक्त पुरवठा केलेले चार्जर वापरा."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"USB चार्जिंग समर्थित नाही."</string>
@@ -67,14 +73,22 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"सध्या या डीव्हाइसमध्ये साइन इन केलेला वापरकर्ता USB डीबग करणे चालू करू शकत नाही. हे वैशिष्ट्य वापरण्यासाठी, प्राथमिक वापरकर्त्यावर स्विच करा."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"स्क्रीन भरण्यासाठी झूम करा"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"स्क्रीन भरण्यासाठी ताणा"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"स्क्रीनशॉट जतन करत आहे…"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"स्क्रीनशॉट जतन करत आहे…"</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"स्क्रीनशॉट जतन केला जात आहे."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"स्क्रीनशॉट कॅप्चर केला."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"आपला स्क्रीनशॉट पाहण्यासाठी टॅप करा."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"स्क्रीनशॉट कॅप्चर करू शकलो नाही."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"स्क्रीनशॉट जतन करताना समस्या आली."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"मर्यादित संचय जागेमुळे स्क्रीनशॉट जतन करू शकत नाही."</string>
+ <!-- no translation found for screenshot_saving_text (2545047868936087248) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_title (5637073968117370753) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_text (7574667448002050363) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_title (9096484883063264803) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) -->
+ <skip />
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"अॅप किंवा आपल्या संस्थेद्वारे स्क्रीनशॉट घेण्याची अनुमती नाही"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB फाईल स्थानांतरण पर्याय"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"मीडिया प्लेअर म्हणून माउंट करा (MTP)"</string>
@@ -492,8 +506,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"ब्लूटूथ"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"दुहेरी एकाधिक टोन वारंंवारता"</string>
<string name="stream_accessibility" msgid="301136219144385106">"प्रवेशयोग्यता"</string>
- <!-- no translation found for ring_toggle_title (3281244519428819576) -->
- <skip />
+ <string name="ring_toggle_title" msgid="3281244519428819576">"कॉल"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"रिंग करा"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"कंपन"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"म्युट करा"</string>
@@ -504,6 +517,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. नि:शब्द करण्यासाठी टॅप करा."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s आवाज नियंत्रणे दर्शविली. डिसमिस करण्यासाठी वर स्वाइप करा."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"आवाज नियंत्रणे लपविली"</string>
+ <string name="output_title" msgid="5355078100792942802">"मीडिया आउटपुट"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"फोन कॉल आउटपुट"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"कोणतीही डिव्हाइस सापडली नाहीत"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"कोणतीही डिव्हाइस सापडली नाहीत. <xliff:g id="SERVICE">%1$s</xliff:g> चालू करून पाहा"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"ब्लुटूथ"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"वाय-फाय"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"ब्लुटूथ आणि वाय-फाय"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"सिस्टम UI ट्युनर"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"एम्बेडेड बॅटरी टक्केवारी दर्शवा"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"चार्ज होत नसताना स्टेटस बार चिन्हामध्ये बॅटरी पातळी टक्केवारी दर्शवा"</string>
@@ -551,26 +571,27 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"बंद"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"पॉवर सूचना नियंत्रणांच्या साहाय्याने तुम्ही अॅप सूचनांसाठी 0 ते 5 असे महत्त्व स्तर सेट करू शकता. \n\n"<b>"स्तर 5"</b>" \n- सूचना सूचीच्या शीर्षस्थानी दाखवा \n- पूर्ण स्क्रीन व्यत्ययास अनुमती द्या \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 4"</b>\n" - पूर्ण स्क्रीन व्यत्ययास प्रतिबंधित करा \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 3"</b>" \n- पूर्ण स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n\n"<b>"स्तर 2"</b>" \n- पूर्ण स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा कंपन करू नका \n\n"<b>"स्तर 1"</b>\n"- पूर्ण स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा कंपन करू नका \n- लॉक स्क्रीन आणि स्टेटस बार मधून लपवा \n- सूचना सूचीच्या तळाशी दर्शवा \n\n"<b>"स्तर 0"</b>" \n- अॅपमधील सर्व सूचना ब्लॉक करा"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"सूचना"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"तुम्हाला यापुढे या सूचना प्राप्त होणार नाहीत"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> सूचना श्रेण्या"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"या अॅपला सूचना श्रेण्या नाहीत"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"या अॅपकडून येणार्या सूचना बंद ठेवता येणार नाहीत"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="one">या अॅपकडील <xliff:g id="NUMBER_1">%s</xliff:g> पैकी 1 सूचना श्रेणी</item>
- <item quantity="other">या अॅपकडील <xliff:g id="NUMBER_1">%s</xliff:g> पैकी 1 सूचना श्रेणी</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, आणि <xliff:g id="NUMBER_5">%3$d</xliff:g> इतर</item>
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, आणि <xliff:g id="NUMBER_5">%3$d</xliff:g> इतर</item>
- </plurals>
+ <!-- no translation found for notification_channel_disabled (344536703863700565) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing (8945102997083836858) -->
+ <skip />
+ <!-- no translation found for inline_stop_button (4172980096860941033) -->
+ <skip />
+ <!-- no translation found for inline_keep_button (6665940297019018232) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing_app (1723113469580031041) -->
+ <skip />
+ <!-- no translation found for notification_unblockable_desc (1037434112919403708) -->
+ <skip />
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी सूचना नियंत्रणे खुली आहेत"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी सूचना नियंत्रणे बंद आहेत"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"या चॅनेलकडील सूचनांना मान्यता द्या"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"सर्व श्रेण्या"</string>
<string name="notification_more_settings" msgid="816306283396553571">"अधिक सेटिंग्ज"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"सानुकूल करा: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <!-- no translation found for notification_app_settings (420348114670768449) -->
+ <skip />
<string name="notification_done" msgid="5279426047273930175">"पूर्ण झाले"</string>
+ <!-- no translation found for inline_undo (558916737624706010) -->
+ <skip />
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"सूचना नियंत्रणे"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"सूचना स्नूझ पर्याय"</string>
@@ -725,6 +746,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"विस्तृत करा"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"लहान करा"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"बंद करा"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"सेटिंग्ज"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"डिसमिस करण्यासाठी खाली ड्रॅग करा"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"मेनू"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> चित्रामध्ये चित्र मध्ये आहे"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index e074875..118d2c7 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Ketik untuk meredam."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s kawalan kelantangan ditunjukkan. Leret ke atas untuk mengetepikan."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Kawalan kelantangan disembunyikan"</string>
+ <string name="output_title" msgid="5355078100792942802">"Output media"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Output panggilan telefon"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Tiada peranti ditemui"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Tiada peranti ditemui. Cuba hidupkan <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth dan Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Penala UI Sistem"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Tunjukkan peratusan bateri terbenam"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Tunjukkan peratusan aras bateri dalam ikon bar status semasa tidak mengecas"</string>
@@ -724,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Kembangkan"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimumkan"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Tutup"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Seret ke bawah untuk mengetepikan"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> terdapat dalam gambar dalam gambar"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 22375b4..ac6adb8 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -33,7 +33,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"လက်ရှိအသုံးပြုမှု"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"အကြောင်းကြားချက်များ။"</string>
<string name="battery_low_title" msgid="6456385927409742437">"ဘက်ထရီ အားနည်းနေ"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"ဘက်ထရီ အားနည်းနေပါပြီ။ ဘက်ထရီအားထိန်းကို ဖွင့်ပါ"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> ကျန်ရှိနေ"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> ကျန်သည်၊ သင့်အသုံးပြုမှုအရ <xliff:g id="TIME">%s</xliff:g> ခန့် ကျန်ပါသည်"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> ကျန်သည်၊ <xliff:g id="TIME">%s</xliff:g> ခန့် ကျန်ပါသည်"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> ကျန်ပါတယ်။ ဘက်ထရီ အားထိန်းကို ဖွင့်ထားသည်။"</string>
<string name="invalid_charger" msgid="4549105996740522523">"လက်ရှိUSBအားသွင်းခြင်း အသုံးမပြုနိုင်ပါ \n ပေးထားသောအားသွင်းကိရိယာကိုသာ အသုံးပြုပါ"</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"USB အားသွင်းမှု မပံ့ပိုးပါ။"</string>
@@ -67,14 +70,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"ဤစက်ပစ္စည်းသို့ လက်ရှိဝင်ရောက်ထားသည့် အသုံးပြုသူသည် USB အမှားပြင်ဆင်ခြင်းကို ဖွင့်၍မရပါ။ ဤဝန်ဆောင်မှုကို အသုံးပြုရန် အဓိကအသုံးပြုသူအဖြစ်သို့ ပြောင်းပါ။"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"ဇူးမ်အပြည့်ဆွဲခြင်း"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"ဖန်သားပြင်အပြည့်ဆန့်ခြင်း"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"ဖန်သားပြင်ဓာတ်ပုံ"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"ဖန်သားပြင်ဓါတ်ပုံသိမ်းစဉ်.."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"ဖန်သားပြင်ဓါတ်ပုံရိုက်ခြင်းအား သိမ်းဆည်းပါမည်"</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"ဖန်သားပြင်ဓါတ်ပုံရိုက်ခြင်းအား သိမ်းဆည်းပြီးပါပြီ"</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"ဖန်သားပြင်ဓါတ်ပုံရိုက်ခြင်းအား ဖမ်းယူပြီး"</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"မျက်နှာပြင်ပုံဖမ်းယူခြင်းကို ကြည့်ရှုရန် တို့ပါ"</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"ဖန်သားပြင်ဓါတ်ပုံရိုက်ခြင်းအား မဖမ်းစီးနိုင်ပါ"</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"ဖန်သားပြင်ဓာတ်ပုံဖမ်းယူမှုကို သိမ်းဆည်းရာတွင် ပြဿနာကြုံခဲ့သည်။"</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"သိုလှောင်ခန်းနေရာ အကန့်အသတ်ရှိသောကြောင့် ဖန်သားပြင်ဓာတ်ပုံကို သိမ်းဆည်း၍မရပါ။"</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"ဖန်သားပြင်ဓါတ်ပုံကို သိမ်းနေသည်"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"ဖန်သားပြင်ဓာတ်ပုံကို သိမ်းပြီးပါပြီ"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"ဖန်သားပြင်ဓာတ်ပုံကို ကြည့်ရန် တို့ပါ"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"ဖန်သားပြင်ဓါတ်ပုံ ရိုက်၍မရပါ"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"ဖန်သားပြင်ဓာတ်ပုံကို သိမ်းရာတွင် ပြဿနာရှိနေသည်"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"သိုလှောင်ခန်းနေရာ အကန့်အသတ်ရှိသောကြောင့် ဖန်သားပြင်ဓာတ်ပုံကို သိမ်းဆည်း၍မရပါ"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"ဖန်သားပြင်ဓာတ်ပုံရိုက်ကူးခြင်းကို ဤအက်ပ် သို့မဟုတ် သင်၏အဖွဲ့အစည်းက ခွင့်မပြုပါ"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB ဖိုင်ပြောင်း ရွေးမှုများ"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"မီဒီယာပလေရာအနေဖြင့် တပ်ဆင်ရန် (MTP)"</string>
@@ -492,6 +496,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"ဘလူးတုသ်"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"နှစ်လိုင်းပေါင်း အသံပေါင်းစုံ ကြိမ်နှုန်း"</string>
<string name="stream_accessibility" msgid="301136219144385106">"အများသုံးစွဲနိုင်မှု"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"ခေါ်ဆိုမှုများ"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"အသံမြည်သည်"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"တုန်ခါသည်"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"အသံတိတ်သည်"</string>
@@ -502,6 +507,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s။ အသံတိတ်ရန် တို့ပါ။"</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"အသံအတိုးအလျှော့ခလုတ် %s ပြသထားပါသည်။ ပယ်ရန် အပေါ်သို့ပွတ်ဆွဲပါ။"</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"အသံအတိုးအလျှော့ခလုတ်များကို ဝှက်ထားပါသည်"</string>
+ <string name="output_title" msgid="5355078100792942802">"မီဒီယာ အထွက်"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"ဖုန်းလိုင်း အထွက်"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"မည်သည့် စက်ပစ္စည်းမျှ မတွေ့ပါ"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"မည်သည့် စက်ပစ္စည်းမျှ မတွေ့ပါ။ <xliff:g id="SERVICE">%1$s</xliff:g> ကို ဖွင့်ကြည့်ပါ"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"ဘလူးတုသ်"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi−Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"ဘလူးတုသ်နှင့် Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"စနစ် UI ဖမ်းစက်"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"မြုတ်ထားသည့် ဘက်ထရီ ရာခိုင်နှုန်းကို ပြပါ"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"အားမသွင်းနေစဉ်တွင် ဘတ်ထရီအဆင့် ရာခိုင်နှုန်းကို အခြေနေပြဘား အိုင်ကွန်တွင် ပြပါ"</string>
@@ -549,26 +561,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ပိတ်ပါ"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"ပါဝါအကြောင်းကြားချက် ထိန်းချုပ်မှုများကိုအသုံးပြုပြီး အက်ပ်တစ်ခု၏ အကြောင်းကြားချက် အရေးပါမှု ၀ မှ ၅ အထိသတ်မှတ်ပေးနိုင်သည်။ \n\n"<b>"အဆင့် ၅"</b>" \n- အကြောင်းကြားချက်စာရင်း၏ ထိပ်ဆုံးတွင် ပြသည် \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်းကို ခွင့်ပြုသည် \n- အမြဲတမ်း ခေတ္တပြပါမည် \n\n"<b>"အဆင့် ၄"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- အမြဲတမ်း ခေတ္တပြပါမည် \n\n"<b>"အဆင့် ၃"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- ဘယ်တော့မှ ခေတ္တပြခြင်း မရှိပါ \n\n"<b>"အဆင့် ၂"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- ဘယ်တော့မှ ခေတ္တပြခြင်း မရှိပါ \n- အသံမြည်ခြင်းနှင့် တုန်ခါခြင်းများ ဘယ်တော့မှ မပြုလုပ်ပါ \n\n"<b>"အဆင့် ၁"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- ဘယ်တော့မှ ခေတ္တပြခြင်း မရှိပါ \n- အသံမြည်ခြင်းနှင့် တုန်ခါခြင်းများ ဘယ်တော့မှ မပြုလုပ်ပါ \n- လော့ခ်ချထားသည့် မျက်နှာပြင်နှင့် အခြေအနေဘားတန်းတို့တွင် မပြပါ \n- အကြောင်းကြားချက်စာရင်း အောက်ဆုံးတွင်ပြသည် \n\n"<b>"အဆင့် ၀"</b>" \n- အက်ပ်မှ အကြောင်းကြားချက်များ အားလုံးကို ပိတ်ဆို့သည်"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"အကြောင်းကြားချက်များ"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"သင်သည် ဤအကြောင်းကြားချက်များကို နောက်ထပ် လက်ခံရရှိတော့မည် မဟုတ်ပါ"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"အကြောင်းကြားချက် အမျိုးအစား <xliff:g id="NUMBER">%d</xliff:g> ခု"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"ဤအက်ပ်တွင် အကြောင်းကြားချက် အမျိုးအစားများ မရှိပါ"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"ဤအက်ပ်မှပို့သော အကြောင်းကြားချက်များကို ပိတ်ထား၍မရပါ"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="other">ဤအက်ပ်ရှိ အကြောင်းကြားချက်အမျိုးအစား <xliff:g id="NUMBER_1">%s</xliff:g> ခု အနက်မှ ၁ ခု</item>
- <item quantity="one">ဤအက်ပ်ရှိ အကြောင်းကြားချက်အမျိုးအစား <xliff:g id="NUMBER_0">%s</xliff:g> ခု အနက်မှ ၁ ခု</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>၊ <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>၊ <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> နှင့် <xliff:g id="NUMBER_5">%3$d</xliff:g> အခြား</item>
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>၊ <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> နှင့် <xliff:g id="NUMBER_2">%3$d</xliff:g> အခြား</item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"ဤအကြောင်းကြားချက်များကို မြင်ရတော့မည် မဟုတ်ပါ"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"ဤအကြောင်းကြားချက်များကို ဆက်ပြလိုပါသလား။"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"အကြောင်းကြားချက်များကို ရပ်ရန်"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"ဆက်ပြရန်"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"ဤအက်ပ်ထံမှ အကြောင်းကြားချက်များကို ဆက်ပြလိုပါသလား။"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"ဤအကြောင်းကြားချက်များကို ပိတ်၍မရပါ"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် အကြောင်းကြားချက်ထိန်းချုပ်မှုများကို ဖွင့်ထားသည်"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် အကြောင်းကြားချက်ထိန်းချုပ်မှုများကို ပိတ်ထားသည်"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"ဤချန်နယ်မှ အကြောင်းကြားချက်များကို ခွင့်ပြုပါ"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"အုပ်စုအားလုံး"</string>
<string name="notification_more_settings" msgid="816306283396553571">"နောက်ထပ် ဆက်တင်များ"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"စိတ်ကြိုက်သတ်မှတ်ရန်− <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"စိတ်ကြိုက်ပြုလုပ်ရန်"</string>
<string name="notification_done" msgid="5279426047273930175">"ပြီးပါပြီ"</string>
+ <string name="inline_undo" msgid="558916737624706010">"တစ်ဆင့်နောက်ပြန်ရန်"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"အကြောင်းကြားချက် ထိန်းချုပ်မှုများ"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"အကြောင်းကြားချက်များကို ဆိုင်းငံ့ရန် ရွေးချယ်စရာများ"</string>
@@ -723,6 +728,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"ချဲ့ရန်"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"ချုံ့ရန်"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"ပိတ်ရန်"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"ဆက်တင်များ"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"ပယ်ရန်အတွက် အောက်သို့ ပွတ်ဆွဲပါ"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"မီနူး"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> သည် တစ်ခုပေါ် တစ်ခုထပ်၍ ဖွင့်ထားသည်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 661df03..1afd635 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -33,7 +33,13 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Aktiviteter"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Varsler"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Batterikapasiteten er lav"</string>
+ <!-- no translation found for battery_low_title_hybrid (6268991275887381595) -->
+ <skip />
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> gjenstår"</string>
+ <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) -->
+ <skip />
+ <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) -->
+ <skip />
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> gjenstår. Batterisparing er på."</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB-lading støttes ikke.\nBruk kun den medfølgende laderen."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"Lading via USB støttes ikke."</string>
@@ -67,14 +73,22 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Brukeren som for øyeblikket er logget på denne enheten, kan ikke slå på USB-feilsøking. For å bruke denne funksjonen, bytt til hovedbrukeren."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom for å fylle skjermen"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Strekk for å fylle skjerm"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Lagrer skjermdumpen …"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Lagrer skjermdumpen …"</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Skjermdumpen lagres."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Skjermdumpen er lagret."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Trykk for å se skjermdumpen."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Kan ikke lagre skjermdumpen."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Det oppsto et problem under lagring av skjermdumpen."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Kan ikke lagre skjermdumpen på grunn av begrenset lagringsplass."</string>
+ <!-- no translation found for screenshot_saving_text (2545047868936087248) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_title (5637073968117370753) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_text (7574667448002050363) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_title (9096484883063264803) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) -->
+ <skip />
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Appen eller organisasjonen din tillater ikke at du tar skjermdumper"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Altern. for USB-filoverføring"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Sett inn som mediespiller (MTP)"</string>
@@ -503,6 +517,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Trykk for å slå av lyden."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Volumkontrollene for %s vises. Sveip opp for å avvise dem."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Volumkontrollene er skjult"</string>
+ <string name="output_title" msgid="5355078100792942802">"Medieutdata"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Utgang for telefonsamtaler"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Fant ingen enheter"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Fant ingen enheter. Prøv å slå på <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth og Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Vis prosent for det innebygde batteriet"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Vis batterinivåprosenten inni statusfeltikonet når du ikke lader"</string>
@@ -550,26 +571,27 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Av"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"Med effektive varselinnstillinger kan du angi viktighetsnivåer fra 0 til 5 for appvarsler. \n\n"<b>"Nivå 5"</b>" \n– Vis øverst på varsellisten \n– Tillat forstyrrelser ved fullskjermmodus \n– Vis alltid raskt \n\n"<b>"Nivå 4"</b>" \n– Forhindre forstyrrelser ved fullskjermmodus \n– Vis alltid raskt \n\n"<b>"Nivå 3"</b>" \n– Forhindre forstyrrelser ved fullskjermmodus \n– Vis aldri raskt \n\n"<b>"Nivå 2"</b>" \n– Forhindre forstyrrelser ved fullskjermmodus \n– Vis aldri fort \n– Tillat aldri lyder eller vibrering \n\n"<b>"Nivå 1"</b>" \n– Forhindre forstyrrelser ved fullskjermmodus \n– Vis aldri raskt \n– Tillat aldri lyder eller vibrering \n– Skjul fra låseskjermen og statusfeltet \n– Vis nederst på varsellisten \n\n"<b>"Nivå 0"</b>" \n– Blokkér alle varsler fra appen"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Varsler"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"Du får ikke disse varslene lenger"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> varselkategorier"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"Denne appen har ikke varselkategorier"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Varsler fra denne appen kan ikke slås av"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="other">1 av <xliff:g id="NUMBER_1">%s</xliff:g> varselkategorier fra denne appen</item>
- <item quantity="one">1 av <xliff:g id="NUMBER_0">%s</xliff:g> varselkategori fra denne appen</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> og <xliff:g id="NUMBER_5">%3$d</xliff:g> andre</item>
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> og <xliff:g id="NUMBER_2">%3$d</xliff:g> annen</item>
- </plurals>
+ <!-- no translation found for notification_channel_disabled (344536703863700565) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing (8945102997083836858) -->
+ <skip />
+ <!-- no translation found for inline_stop_button (4172980096860941033) -->
+ <skip />
+ <!-- no translation found for inline_keep_button (6665940297019018232) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing_app (1723113469580031041) -->
+ <skip />
+ <!-- no translation found for notification_unblockable_desc (1037434112919403708) -->
+ <skip />
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Varselinnstillingene for <xliff:g id="APP_NAME">%1$s</xliff:g> er åpnet"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Varselinnstillingene for <xliff:g id="APP_NAME">%1$s</xliff:g> er lukket"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Tillat varsler fra denne kanalen"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"Alle kategorier"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Flere innstillinger"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Tilpass: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <!-- no translation found for notification_app_settings (420348114670768449) -->
+ <skip />
<string name="notification_done" msgid="5279426047273930175">"Ferdig"</string>
+ <!-- no translation found for inline_undo (558916737624706010) -->
+ <skip />
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"varselinnstillinger"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"slumrealternativer for varsler"</string>
@@ -724,6 +746,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Vis"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimer"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Lukk"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Innstillinger"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Dra ned for å avvise"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Meny"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> er i bilde-i-bilde"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index dce9cc4..a5c13f1 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -33,7 +33,13 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"चलिरहेको"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"सूचनाहरू"</string>
<string name="battery_low_title" msgid="6456385927409742437">"ब्याट्री कम छ"</string>
+ <!-- no translation found for battery_low_title_hybrid (6268991275887381595) -->
+ <skip />
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> बाँकी"</string>
+ <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) -->
+ <skip />
+ <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) -->
+ <skip />
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> बाँकी। ब्याट्री सेभर सक्रिय छ।"</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB चार्ज गर्न समर्थित छैन।\n आपूर्ति गरिएको चार्जर मात्र प्रयोग गर्नुहोस्।"</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"USB चार्ज समर्थित छैन।"</string>
@@ -67,14 +73,22 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"हाल यस यन्त्रमा साइन इन हुनुभएको प्रयोगकर्ताले USB डिबग सक्रिय गर्न सक्नुहुन्न। यो सुविधाको प्रयोग गर्न प्राथमिक प्रयोगकर्तामा बदल्नुहोस्।"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"स्क्रिन भर्न जुम गर्नुहोस्"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"स्क्रिन भर्न तन्काउनुहोस्"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"स्क्रिनसट बचत गर्दै…"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"स्क्रिनसट बचत गर्दै…"</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"स्क्रिनसट बचत हुँदैछ।"</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"स्क्रिनसट क्याप्चर गरियो।"</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"आफ्नो स्क्रिनसट हेर्न ट्याप गर्नुहोस्।"</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"स्क्रिनसट क्याप्चर गर्न सकिएन।"</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"स्क्रिनसटलाई सुरक्षित गर्दा समस्या भयो।"</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"सीमित भण्डारण स्थान उपलब्ध रहेको हुनाले स्क्रिनसटलाई सुरक्षित गर्न सकिँदैन।"</string>
+ <!-- no translation found for screenshot_saving_text (2545047868936087248) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_title (5637073968117370753) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_text (7574667448002050363) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_title (9096484883063264803) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) -->
+ <skip />
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"उक्त अनुप्रयोग वा तपाईंको संगठनले स्क्रिनसटहरू लिन दिँदैन"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB फाइल सार्ने विकल्पहरू"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"मिडिया प्लेयर(MTP)को रूपमा माउन्ट गर्नुहोस्"</string>
@@ -492,8 +506,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"ब्लुटुथ"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"दोहोरो बहु टोनको फ्रिक्वेन्सी"</string>
<string name="stream_accessibility" msgid="301136219144385106">"पहुँच"</string>
- <!-- no translation found for ring_toggle_title (3281244519428819576) -->
- <skip />
+ <string name="ring_toggle_title" msgid="3281244519428819576">"कलहरू"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"घन्टी"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"कम्पन"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"म्युट गर्नुहोस्"</string>
@@ -504,6 +517,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s। म्यूट गर्न ट्याप गर्नुहोस्।"</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s का भोल्युम सम्बन्धी नियन्त्रणहरूलाई देखाइएको छ। खारेज गर्नका लागि स्वाइप गर्नुहोस्।"</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"भोल्युम सम्बन्धी नियन्त्रणहरूलाई लुकाइयो"</string>
+ <string name="output_title" msgid="5355078100792942802">"मिडियाको आउटपुट"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"फोन कलको आउटपुट"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"कुनै पनि यन्त्र भेटिएन"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"कुनै पनि यन्त्र भेटिएन। <xliff:g id="SERVICE">%1$s</xliff:g> सक्रिय गरी हेर्नुहोस्"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"ब्लुटुथ"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"ब्लुटुथ र Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"प्रणाली UI ट्युनर"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"इम्बेड गरिएको ब्याट्री प्रतिशत देखाउनुहोस्"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"चार्ज नगरेको बेला वस्तुस्थिति पट्टी आइकन भित्र ब्याट्री प्रतिशत स्तर देखाउनुहोस्"</string>
@@ -551,26 +571,27 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"निष्क्रिय"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"सशक्त सूचना नियन्त्रणहरू मार्फत तपाईं अनुप्रयाेगका सूचनाहरूका लागि ० देखि ५ सम्मको महत्व सम्बन्धी स्तर सेट गर्न सक्नुहुन्छ। \n\n"<b>"स्तर ५"</b>" \n- सूचनाको सूचीको माथिल्लो भागमा देखाउने \n- पूर्ण स्क्रिनमा अवरोधका लागि अनुमति दिने \n- सधैँ चियाउने \n\n"<b>"स्तर ४"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- सधैँ चियाउने \n\n"<b>"स्तर ३"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- कहिल्यै नचियाउने \n\n"<b>"स्तर २"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- कहिल्यै नचियाउने \n- कहिल्यै पनि आवाज ननिकाल्ने र कम्पन नगर्ने \n\n"<b>"स्तर १"</b>" \n- पूर्ण स्क्रिनमा अवरोध रोक्ने \n- कहिल्यै नचियाउने \n- कहिल्यै पनि आवाज ननिकाल्ने वा कम्पन नगर्ने \n- लक स्क्रिन र वस्तुस्थिति पट्टीबाट लुकाउने \n- सूचनाको सूचीको तल्लो भागमा देखाउने \n\n"<b>"स्तर ०"</b>" \n- अनुप्रयोगका सबै सूचनाहरूलाई रोक्ने"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"सूचनाहरू"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"तपाईंले अब उप्रान्त यी सूचनाहरू प्राप्त गर्नुहुने छैन"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> सूचनाका कोटिहरू"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"यस अनुप्रयोगमा सूचना सम्बन्धी कोटीहरू छैनन्"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"यस अनुप्रयोगका सूचनाहरूलाई निष्क्रिय पार्न सकिँदैन"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="other">यस अनुप्रयोगका <xliff:g id="NUMBER_1">%s</xliff:g> सूचना कोटिहरू मध्ये १</item>
- <item quantity="one"> यस अनुप्रयोगको <xliff:g id="NUMBER_0">%s</xliff:g> सूचना कोटी मध्ये १</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> र <xliff:g id="NUMBER_5">%3$d</xliff:g> अन्य</item>
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> र <xliff:g id="NUMBER_2">%3$d</xliff:g> अन्य</item>
- </plurals>
+ <!-- no translation found for notification_channel_disabled (344536703863700565) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing (8945102997083836858) -->
+ <skip />
+ <!-- no translation found for inline_stop_button (4172980096860941033) -->
+ <skip />
+ <!-- no translation found for inline_keep_button (6665940297019018232) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing_app (1723113469580031041) -->
+ <skip />
+ <!-- no translation found for notification_unblockable_desc (1037434112919403708) -->
+ <skip />
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> का सूचना सम्बन्धी नियन्त्रणहरूलाई खोलियो"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> का सूचना सम्बन्धी नियन्त्रणहरूलाई बन्द गरियो"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"यो च्यानलका सूचनाहरूलाई अनुमति दिनुहोस्"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"सबै कोटिहरू"</string>
<string name="notification_more_settings" msgid="816306283396553571">"थप सेटिङहरू"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"आफू अनुकूल पार्नुहोस्: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <!-- no translation found for notification_app_settings (420348114670768449) -->
+ <skip />
<string name="notification_done" msgid="5279426047273930175">"सम्पन्न भयो"</string>
+ <!-- no translation found for inline_undo (558916737624706010) -->
+ <skip />
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"सूचना सम्बन्धी नियन्त्रणहरू"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"सूचना स्नुज गर्ने विकल्पहरू"</string>
@@ -725,6 +746,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"विस्तृत गर्नुहोस्"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"सानो बनाउनुहोस्"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"बन्द गर्नुहोस्"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"सेटिङहरू"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"खारेज गर्न तल तान्नुहोस्"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"मेनु"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> Picture-in-picture मा छ"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index ca0fa07..7139a63 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -199,7 +199,7 @@
<string name="accessibility_quick_settings_airplane_changed_on" msgid="8983005603505087728">"Vliegtuigmodus ingeschakeld."</string>
<string name="accessibility_quick_settings_dnd_priority_on" msgid="1448402297221249355">"Niet storen aan, alleen prioriteit."</string>
<string name="accessibility_quick_settings_dnd_none_on" msgid="6882582132662613537">"Niet storen aan, totale stilte."</string>
- <string name="accessibility_quick_settings_dnd_alarms_on" msgid="9152834845587554157">"Niet storen aan, alleen alarmen."</string>
+ <string name="accessibility_quick_settings_dnd_alarms_on" msgid="9152834845587554157">"Niet storen aan, alleen wekkers."</string>
<string name="accessibility_quick_settings_dnd" msgid="6607873236717185815">"Niet storen."</string>
<string name="accessibility_quick_settings_dnd_off" msgid="2371832603753738581">"Niet storen uit."</string>
<string name="accessibility_quick_settings_dnd_changed_off" msgid="898107593453022935">"Niet storen uitgeschakeld."</string>
@@ -215,7 +215,7 @@
<string name="accessibility_quick_settings_location_on" msgid="5809937096590102036">"Locatiemelding aan."</string>
<string name="accessibility_quick_settings_location_changed_off" msgid="8526845571503387376">"Locatiemelding uitgeschakeld."</string>
<string name="accessibility_quick_settings_location_changed_on" msgid="339403053079338468">"Locatiemelding ingeschakeld."</string>
- <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm is ingesteld op <xliff:g id="TIME">%s</xliff:g>."</string>
+ <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Wekker is ingesteld op <xliff:g id="TIME">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_close" msgid="3115847794692516306">"Paneel sluiten."</string>
<string name="accessibility_quick_settings_more_time" msgid="3659274935356197708">"Meer tijd."</string>
<string name="accessibility_quick_settings_less_time" msgid="2404728746293515623">"Minder tijd."</string>
@@ -265,7 +265,7 @@
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_dnd_label" msgid="8735855737575028208">"Niet storen"</string>
<string name="quick_settings_dnd_priority_label" msgid="483232950670692036">"Alleen prioriteit"</string>
- <string name="quick_settings_dnd_alarms_label" msgid="2559229444312445858">"Alleen alarmen"</string>
+ <string name="quick_settings_dnd_alarms_label" msgid="2559229444312445858">"Alleen wekkers"</string>
<string name="quick_settings_dnd_none_label" msgid="5025477807123029478">"Totale stilte"</string>
<string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"Bluetooth"</string>
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> apparaten)"</string>
@@ -348,11 +348,11 @@
<string name="description_target_search" msgid="3091587249776033139">"Zoeken"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Veeg omhoog voor <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
<string name="description_direction_left" msgid="7207478719805562165">"Veeg naar links voor <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
- <string name="zen_priority_introduction" msgid="1149025108714420281">"Je wordt niet gestoord door geluiden en trillingen, behalve bij alarmen, herinneringen, afspraken en specifieke bellers die je selecteert. Je kunt nog steeds alles horen wat je wilt afspelen, waaronder muziek, video\'s en games."</string>
- <string name="zen_alarms_introduction" msgid="4934328096749380201">"Je wordt niet gestoord door geluiden en trillingen, behalve bij alarmen. Je kunt nog steeds alles horen wat je wilt afspelen, waaronder muziek, video\'s en games."</string>
+ <string name="zen_priority_introduction" msgid="1149025108714420281">"Je wordt niet gestoord door geluiden en trillingen, behalve bij wekkers, herinneringen, afspraken en specifieke bellers die je selecteert. Je kunt nog steeds alles horen wat je wilt afspelen, waaronder muziek, video\'s en games."</string>
+ <string name="zen_alarms_introduction" msgid="4934328096749380201">"Je wordt niet gestoord door geluiden en trillingen, behalve bij wekkers. Je kunt nog steeds alles horen wat je wilt afspelen, waaronder muziek, video\'s en games."</string>
<string name="zen_priority_customize_button" msgid="7948043278226955063">"Aanpassen"</string>
- <string name="zen_silence_introduction_voice" msgid="3948778066295728085">"Hiermee worden ALLE geluiden en trillingen geblokkeerd, waaronder die voor alarmen, muziek, video\'s en games. Je kunt wel nog steeds bellen."</string>
- <string name="zen_silence_introduction" msgid="3137882381093271568">"Hiermee worden ALLE geluiden en trillingen geblokkeerd, waaronder die voor alarmen, muziek, video\'s en games."</string>
+ <string name="zen_silence_introduction_voice" msgid="3948778066295728085">"Hiermee worden ALLE geluiden en trillingen geblokkeerd, waaronder die voor wekkers, muziek, video\'s en games. Je kunt wel nog steeds bellen."</string>
+ <string name="zen_silence_introduction" msgid="3137882381093271568">"Hiermee worden ALLE geluiden en trillingen geblokkeerd, waaronder die voor wekkers, muziek, video\'s en games."</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Minder urgente meldingen onderaan"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Tik nog eens om te openen"</string>
@@ -365,7 +365,7 @@
<string name="interruption_level_none_with_warning" msgid="5114872171614161084">"Totale stilte. Hiermee worden schermlezers ook op stil gezet."</string>
<string name="interruption_level_none" msgid="6000083681244492992">"Totale stilte"</string>
<string name="interruption_level_priority" msgid="6426766465363855505">"Alleen prioriteit"</string>
- <string name="interruption_level_alarms" msgid="5226306993448328896">"Alleen alarmen"</string>
+ <string name="interruption_level_alarms" msgid="5226306993448328896">"Alleen wekkers"</string>
<string name="interruption_level_none_twoline" msgid="3957581548190765889">"Totale\nstilte"</string>
<string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Alleen\nprioriteit"</string>
<string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Alleen\nalarmen"</string>
@@ -487,11 +487,12 @@
<string name="stream_system" msgid="7493299064422163147">"Systeem"</string>
<string name="stream_ring" msgid="8213049469184048338">"Bellen"</string>
<string name="stream_music" msgid="9086982948697544342">"Media"</string>
- <string name="stream_alarm" msgid="5209444229227197703">"Alarm"</string>
+ <string name="stream_alarm" msgid="5209444229227197703">"Wekker"</string>
<string name="stream_notification" msgid="2563720670905665031">"Melding"</string>
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Frequentie voor tweevoudige multitoon"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Toegankelijkheid"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Oproepen"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"Bellen"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Trillen"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"Dempen"</string>
@@ -502,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Tik om te dempen."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Volumeknoppen van %s worden weergegeven. Veeg omhoog om te sluiten."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Volumeknoppen verborgen"</string>
+ <string name="output_title" msgid="5355078100792942802">"Media-uitvoer"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Uitvoer van telefoongesprek"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Geen apparaten gevonden"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Geen apparaten gevonden. Probeer <xliff:g id="SERVICE">%1$s</xliff:g> in te schakelen."</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wifi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth en wifi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Systeem-UI-tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Percentage ingebouwde batterij weergeven"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Accupercentage weergeven in het pictogram op de statusbalk wanneer er niet wordt opgeladen"</string>
@@ -512,13 +520,13 @@
<string name="enable_demo_mode" msgid="4844205668718636518">"Demomodus inschakelen"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Demomodus weergeven"</string>
<string name="status_bar_ethernet" msgid="5044290963549500128">"Ethernet"</string>
- <string name="status_bar_alarm" msgid="8536256753575881818">"Alarm"</string>
+ <string name="status_bar_alarm" msgid="8536256753575881818">"Wekker"</string>
<string name="status_bar_work" msgid="6022553324802866373">"Werkprofiel"</string>
<string name="status_bar_airplane" msgid="7057575501472249002">"Vliegtuigmodus"</string>
<string name="add_tile" msgid="2995389510240786221">"Tegel toevoegen"</string>
<string name="broadcast_tile" msgid="3894036511763289383">"Tegel \'Uitzenden\'"</string>
- <string name="zen_alarm_warning_indef" msgid="3482966345578319605">"U hoort je volgende alarm niet <xliff:g id="WHEN">%1$s</xliff:g> tenzij u dit voor die tijd uitschakelt"</string>
- <string name="zen_alarm_warning" msgid="444533119582244293">"U hoort je volgende alarm niet <xliff:g id="WHEN">%1$s</xliff:g>"</string>
+ <string name="zen_alarm_warning_indef" msgid="3482966345578319605">"Je hoort je volgende wekker niet <xliff:g id="WHEN">%1$s</xliff:g> tenzij je dit voor die tijd uitschakelt"</string>
+ <string name="zen_alarm_warning" msgid="444533119582244293">"Je hoort je volgende wekker niet <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="3980063409350522735">"om <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="4242179982586714810">"op <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_quick_settings_detail" msgid="2579369091672902101">"Snelle instellingen, <xliff:g id="TITLE">%s</xliff:g>."</string>
@@ -712,7 +720,7 @@
<string name="accessibility_quick_settings_settings" msgid="6132460890024942157">"Instellingen openen."</string>
<string name="accessibility_quick_settings_expand" msgid="2375165227880477530">"Snelle instellingen openen."</string>
<string name="accessibility_quick_settings_collapse" msgid="1792625797142648105">"Snelle instellingen sluiten."</string>
- <string name="accessibility_quick_settings_alarm_set" msgid="1863000242431528676">"Alarm is ingesteld."</string>
+ <string name="accessibility_quick_settings_alarm_set" msgid="1863000242431528676">"Wekker is ingesteld."</string>
<string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Ingelogd als <xliff:g id="ID_1">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_no_internet" msgid="31890692343084075">"Geen internet."</string>
<string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Details openen."</string>
@@ -723,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Uitvouwen"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimaliseren"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Sluiten"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Sleep omlaag om te sluiten"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> is in scherm-in-scherm"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 43503d6..b65ce97 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -33,7 +33,13 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"ਜਾਰੀ"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"ਸੂਚਨਾਵਾਂ"</string>
<string name="battery_low_title" msgid="6456385927409742437">"ਬੈਟਰੀ ਘੱਟ ਹੈ"</string>
+ <!-- no translation found for battery_low_title_hybrid (6268991275887381595) -->
+ <skip />
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> ਬਾਕੀ"</string>
+ <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) -->
+ <skip />
+ <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) -->
+ <skip />
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> ਬਾਕੀ। ਬੈਟਰੀ ਸੇਵਰ ਚਾਲੂ ਹੈ।"</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB ਚਾਰਜਿੰਗ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ।\nਕੇਵਲ ਸਪਲਾਈ ਕੀਤਾ ਚਾਰਜਰ ਵਰਤੋ।"</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"USB ਚਾਰਜਿੰਗ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ।"</string>
@@ -67,14 +73,22 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to the primary user."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"ਸਕ੍ਰੀਨ ਭਰਨ ਲਈ ਜ਼ੂਮ ਕਰੋ"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"ਸਕ੍ਰੀਨ ਭਰਨ ਲਈ ਸਟ੍ਰੈਚ ਕਰੋ"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਸੁਰੱਖਿਅਤ ਕਰ ਰਿਹਾ ਹੈ…"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਸੁਰੱਖਿਅਤ ਕਰ ਰਿਹਾ ਹੈ…"</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਸੁਰੱਖਿਅਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ।"</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਕੈਪਚਰ ਕੀਤਾ।"</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"ਆਪਣਾ ਸਕ੍ਰੀਨਸ਼ਾਟ ਦੇਖਣ ਲਈ ਟੈਪ ਕਰੋ।"</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਕੈਪਚਰ ਨਹੀਂ ਕਰ ਸਕਿਆ।"</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਰੱਖਿਅਤ ਕਰਨ ਦੌਰਾਨ ਸਮੱਸਿਆ ਆਈ।"</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"ਸਟੋਰੇਜ ਦੀ ਸੀਮਿਤ ਜਗ੍ਹਾ ਹੋਣ ਕਾਰਨ ਸਕ੍ਰੀਨਸ਼ਾਟ ਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਸਕਦਾ।"</string>
+ <!-- no translation found for screenshot_saving_text (2545047868936087248) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_title (5637073968117370753) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_text (7574667448002050363) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_title (9096484883063264803) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) -->
+ <skip />
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"ਐਪ ਜਾਂ ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਸਕ੍ਰੀਨਸ਼ਾਟ ਲੈਣ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੱਤੀ ਗਈ ਹੈ"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB ਫਾਈਲ ਟ੍ਰਾਂਸਫਰ ਚੋਣਾਂ"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"ਇੱਕ ਮੀਡੀਆ ਪਲੇਅਰ (MTP) ਦੇ ਤੌਰ ਤੇ ਮਾਊਂਟ ਕਰੋ"</string>
@@ -492,8 +506,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"ਬਲੂਟੁੱਥ"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"ਦੂਹਰੀ ਮਲਟੀ ਟੋਨ ਆਵਰਤੀ"</string>
<string name="stream_accessibility" msgid="301136219144385106">"ਪਹੁੰਚਯੋਗਤਾ"</string>
- <!-- no translation found for ring_toggle_title (3281244519428819576) -->
- <skip />
+ <string name="ring_toggle_title" msgid="3281244519428819576">"ਕਾਲਾਂ"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"ਘੰਟੀ"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"ਥਰਥਰਾਹਟ"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"ਮਿਊਟ"</string>
@@ -504,6 +517,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s। ਮਿਊਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s ਅਵਾਜ਼ ਕੰਟਰੋਲ ਦਿਖਾਏ ਗਏ ਹਨ। ਖਾਰਜ ਕਰਨ ਲਈ ਉੱਪਰ ਸਵਾਈਪ ਕਰੋ।"</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"ਵੌਲਿਊਮ ਕੰਟਰੋਲ ਲੁਕਾਏ ਗਏ ਹਨ"</string>
+ <string name="output_title" msgid="5355078100792942802">"ਮੀਡੀਆ ਆਊਟਪੁੱਟ"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"ਫ਼ੋਨ ਕਾਲ ਆਊਟਪੁੱਟ"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"ਕੋਈ ਡੀਵਾਈਸ ਨਹੀਂ ਮਿਲੇ"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"ਕੋਈ ਡੀਵਾਈਸ ਨਹੀਂ ਮਿਲੇ। <xliff:g id="SERVICE">%1$s</xliff:g> ਚਾਲੂ ਕਰਨ ਨੂੰ ਅਜ਼ਮਾਓ"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"ਬਲੂਟੁੱਥ"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"ਵਾਈ-ਫਾਈ"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"ਬਲੂਟੁੱਥ ਅਤੇ ਵਾਈ-ਫਾਈ"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI ਟਿਊਨਰ"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"ਜੋਡ਼ੀ ਗਈ ਬੈਟਰੀ ਪ੍ਰਤਿਸ਼ਤਤਾ ਦਿਖਾਓ"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"ਜਦੋਂ ਚਾਰਜ ਨਾ ਹੋ ਰਹੀ ਹੋਵੇ ਤਾਂ ਸਥਿਤੀ ਪੱਟੀ ਪ੍ਰਤੀਕ ਦੇ ਅੰਦਰ ਬੈਟਰੀ ਪੱਧਰ ਪ੍ਰਤਿਸ਼ਤਤਾ ਦਿਖਾਓ"</string>
@@ -551,26 +571,27 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ਬੰਦ"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"ਪਾਵਰ ਸੂਚਨਾ ਕੰਟਰੋਲਾਂ ਨਾਲ, ਤੁਸੀਂ ਕਿਸੇ ਐਪ ਦੀਆਂ ਸੂਚਨਾਵਾਂ ਲਈ ਮਹੱਤਤਾ ਪੱਧਰ ਨੂੰ 0 ਤੋਂ 5 ਤੱਕ ਸੈੱਟ ਕਰ ਸਕਦੇ ਹੋ। \n\n"<b>"ਪੱਧਰ 5"</b>" \n- ਸੂਚਨਾ ਸੂਚੀ ਦੇ ਸਿਖਰ \'ਤੇ ਦਿਖਾਓ \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਦੀ ਆਗਿਆ ਦਿਓ \n- ਹਮੇਸ਼ਾਂ ਝਲਕ ਦਿਖਾਓ \n\n"<b>"ਪੱਧਰ 4"</b>" \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਨੂੰ ਰੋਕੋ \n- ਹਮੇਸ਼ਾਂ ਝਲਕ ਦਿਖਾਓ \n\n"<b>"ਪੱਧਰ 3"</b>" \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਨੂੰ ਰੋਕੋ \n- ਕਦੇ ਝਲਕ ਨਾ ਦਿਖਾਓ \n\n"<b>"ਪੱਧਰ 2"</b>" \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਨੂੰ ਰੋਕੋ \n- ਕਦੇ ਝਲਕ ਨਾ ਦਿਖਾਓ \n- ਕਦੇ ਵੀ ਧੁਨੀ ਜਾਂ ਥਰਥਰਾਹਟ ਨਾ ਕਰੋ \n\n"<b>"ਪੱਧਰ 1"</b>" \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਨੂੰ ਰੋਕੋ \n- ਕਦੇ ਝਲਕ ਨਾ ਦਿਖਾਓ \n- ਕਦੇ ਧੁਨੀ ਜਾਂ ਥਰਥਰਾਹਟ ਨਾ ਕਰੋ \n- ਲਾਕ ਸਕ੍ਰੀਨ ਅਤੇ ਸਥਿਤੀ ਪੱਟੀ ਤੋਂ ਲੁਕਾਓ \n- ਸੂਚਨਾ ਸੂਚੀ ਦੇ ਹੇਠਾਂ ਦਿਖਾਓ \n\n"<b>"ਪੱਧਰ 0"</b>" \n- ਐਪ ਤੋਂ ਸਾਰੀਆਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਬਲਾਕ ਕਰੋ"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"ਸੂਚਨਾਵਾਂ"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"ਤੁਹਾਨੂੰ ਹੁਣ ਇਹ ਸੂਚਨਾਵਾਂ ਪ੍ਰਾਪਤ ਨਹੀਂ ਹੋਣਗੀਆਂ"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> ਸੂਚਨਾ ਸ਼੍ਰੇਣੀਆਂ"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"ਇਸ ਐਪ ਵਿੱਚ ਸੂਚਨਾ ਸ਼੍ਰੇਣੀਆਂ ਨਹੀਂ ਹਨ"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"ਇਸ ਐਪ ਤੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਬੰਦ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="one">ਇਸ ਐਪ ਤੋਂ <xliff:g id="NUMBER_1">%s</xliff:g> ਸੂਚਨਾ ਸ਼੍ਰੇਣੀ ਵਿੱਚੋਂ 1</item>
- <item quantity="other">ਇਸ ਐਪ ਤੋਂ <xliff:g id="NUMBER_1">%s</xliff:g> ਸੂਚਨਾ ਸ਼੍ਰੇਣੀ ਵਿੱਚੋਂ 1</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, ਅਤੇ <xliff:g id="NUMBER_5">%3$d</xliff:g> ਹੋਰ</item>
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, ਅਤੇ <xliff:g id="NUMBER_5">%3$d</xliff:g> ਹੋਰ</item>
- </plurals>
+ <!-- no translation found for notification_channel_disabled (344536703863700565) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing (8945102997083836858) -->
+ <skip />
+ <!-- no translation found for inline_stop_button (4172980096860941033) -->
+ <skip />
+ <!-- no translation found for inline_keep_button (6665940297019018232) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing_app (1723113469580031041) -->
+ <skip />
+ <!-- no translation found for notification_unblockable_desc (1037434112919403708) -->
+ <skip />
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਸੂਚਨਾ ਕੰਟਰੋਲਾਂ ਨੂੰ ਖੋਲ੍ਹਿਆ ਗਿਆ"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਸੂਚਨਾ ਕੰਟਰੋਲਾਂ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"ਇਸ ਚੈਨਲ ਤੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"ਸਭ ਸ਼੍ਰੇਣੀਆਂ"</string>
<string name="notification_more_settings" msgid="816306283396553571">"ਹੋਰ ਸੈਟਿੰਗਾਂ"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"ਵਿਉਂਤਬੱਧ ਕਰੋ: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <!-- no translation found for notification_app_settings (420348114670768449) -->
+ <skip />
<string name="notification_done" msgid="5279426047273930175">"ਹੋ ਗਿਆ"</string>
+ <!-- no translation found for inline_undo (558916737624706010) -->
+ <skip />
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"ਸੂਚਨਾ ਕੰਟਰੋਲ"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"ਸੂਚਨਾ ਸਨੂਜ਼ ਵਿਕਲਪ"</string>
@@ -725,6 +746,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"ਵਿਸਤਾਰ ਕਰੋ"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"ਛੋਟਾ ਕਰੋ"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"ਬੰਦ ਕਰੋ"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"ਸੈਟਿੰਗਾਂ"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"ਖਾਰਜ ਕਰਨ ਲਈ ਹੇਠਾਂ ਘਸੀਟੋ"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"ਮੀਨੂ"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> ਤਸਵੀਰ-ਵਿੱਚ-ਤਸਵੀਰ \'ਚ ਹੈ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index e567e6ba..4c385b7 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -507,6 +507,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Kliknij, by wyciszyć."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Wyświetlane są elementy sterowania głośnością aplikacji %s. Przesuń palcem, by odrzucić."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Elementy sterowania głośnością ukryte"</string>
+ <string name="output_title" msgid="5355078100792942802">"Wyjście multimediów"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Wyjście dla połączeń telefonicznych"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Nie znaleziono urządzeń"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Nie znaleziono urządzeń. Spróbuj włączyć <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth i Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Kalibrator System UI"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Pokaż procent naładowania baterii"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Pokaż procent naładowania baterii w ikonie na pasku stanu, gdy telefon się nie ładuje"</string>
@@ -736,10 +743,12 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Rozwiń"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimalizuj"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Zamknij"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Przeciągnij w dół, by zamknąć"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"Aplikacja <xliff:g id="NAME">%s</xliff:g> działa w trybie obraz w obrazie"</string>
- <string name="pip_notification_message" msgid="5619512781514343311">"Jeśli nie chcesz, by aplikacja <xliff:g id="NAME">%s</xliff:g> korzystała z tej funkcji, otwórz ustawienia i ją wyłącz."</string>
+ <string name="pip_notification_message" msgid="5619512781514343311">"Jeśli nie chcesz, by aplikacja <xliff:g id="NAME">%s</xliff:g> korzystała z tej funkcji, otwórz ustawienia i wyłącz ją."</string>
<string name="pip_play" msgid="1417176722760265888">"Odtwórz"</string>
<string name="pip_pause" msgid="8881063404466476571">"Wstrzymaj"</string>
<string name="pip_skip_to_next" msgid="1948440006726306284">"Dalej"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 61aa901..5fb7b19 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -33,7 +33,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em andamento"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificações"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Bateria fraca"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"A bateria está com pouca carga. Ative a Economia de bateria"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> restantes"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante(s), cerca de <xliff:g id="TIME">%s</xliff:g> com base no seu uso"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante(s), cerca de <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante(s). A Economia de bateria está ativada."</string>
<string name="invalid_charger" msgid="4549105996740522523">"O carregamento via USB não é suportado.\nUse apenas o carregador fornecido."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"O carregamento via USB não é suportado."</string>
@@ -67,14 +70,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"O usuário conectado a este dispositivo não pode ativar a depuração USB. Para usar esse recurso, mude para o usuário principal \"NAME\"."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom p/ preencher a tela"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Ampliar p/ preencher tela"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Captura de tela"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Salvando captura de tela..."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Salvando captura de tela..."</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"A captura de tela está sendo salva."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Captura de tela obtida."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Toque para ver sua captura de tela."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Não foi possível obter a captura de tela."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problema encontrado ao salvar captura de tela."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Não é possível salvar a captura de tela, porque não há espaço suficiente."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"A captura de tela está sendo salva"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"Captura de tela salva"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Toque para ver sua captura de tela"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"Não foi possível fazer a captura de tela"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Problema encontrado ao salvar captura de tela"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Não é possível salvar a captura de tela, porque não há espaço suficiente"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"O app ou a organização não permitem capturas de tela"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opções transf. arq. por USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Conectar como media player (MTP)"</string>
@@ -387,7 +391,7 @@
<string name="guest_exit_guest_dialog_message" msgid="4155503224769676625">"Todos os apps e dados nesta sessão serão excluídos."</string>
<string name="guest_exit_guest_dialog_remove" msgid="7402231963862520531">"Remover"</string>
<string name="guest_wipe_session_title" msgid="6419439912885956132">"Bem-vindo, convidado."</string>
- <string name="guest_wipe_session_message" msgid="8476238178270112811">"Deseja continuar a sessão?"</string>
+ <string name="guest_wipe_session_message" msgid="8476238178270112811">"Quer continuar a sessão?"</string>
<string name="guest_wipe_session_wipe" msgid="5065558566939858884">"Recomeçar"</string>
<string name="guest_wipe_session_dontwipe" msgid="1401113462524894716">"Sim, continuar"</string>
<string name="guest_notification_title" msgid="1585278533840603063">"Usuário convidado"</string>
@@ -494,6 +498,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Multifrequência de dois tons"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Acessibilidade"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Chamadas"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"Tocar"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrar"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"Ignorar"</string>
@@ -504,6 +509,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Toque para silenciar."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s controles de volume exibidos. Deslize para cima para dispensar."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Controles de volume ocultos"</string>
+ <string name="output_title" msgid="5355078100792942802">"Saída de mídia"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Saída de chamada telefônica"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Nenhum dispositivo foi encontrado"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Nenhum dispositivo encontrado. Tente ativar o <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth e Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Sintonizador System UI"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Mostrar porcentagem de bateria incorporada"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Mostrar porcentagem de nível de bateria dentro do ícone da barra de status quando não estiver carregando"</string>
@@ -551,26 +563,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Desativado"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"Com controles de ativação de notificações, é possível definir o nível de importância de 0 a 5 para as notificações de um app. \n\n"<b>"Nível 5"</b>" \n- Exibir na parte superior da lista de notificações \n- Permitir interrupção em tela cheia \n- Sempre exibir \n\n"<b>"Nível 4"</b>" \n- Impedir interrupções em tela cheia \n- Sempre exibir \n\n"<b>"Nível 3"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n\n"<b>"Nível 2"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n- Nunca emitir som ou vibrar \n\n"<b>"Nível 1"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n- Nunca emitir som ou vibrar \n- Ocultar da tela de bloqueio e barra de status \n- Exibir na parte inferior da lista de notificações \n\n"<b>"Nível 0"</b>" \n- Bloquear todas as notificações do app"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notificações"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"Você deixará de receber essas notificações"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> categorias de notificação"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"Este app não tem categorias de notificação"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Notificações deste app não podem ser desativadas"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="one">1 de <xliff:g id="NUMBER_1">%s</xliff:g> categoria de notificação deste app</item>
- <item quantity="other">1 de <xliff:g id="NUMBER_1">%s</xliff:g> categorias de notificação deste app</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> e <xliff:g id="NUMBER_5">%3$d</xliff:g> outro</item>
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> e <xliff:g id="NUMBER_5">%3$d</xliff:g> outros</item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"Você deixará de ver essas notificações"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"Continuar mostrando essas notificações?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"Bloquear notificações"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Continuar mostrando"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuar mostrando notificações desse app?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"Não é possível desativar essas notificações"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Controles de notificação de <xliff:g id="APP_NAME">%1$s</xliff:g> abertos"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Controles de notificação de <xliff:g id="APP_NAME">%1$s</xliff:g> fechados"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Permitir notificações desse canal"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"Todas as categorias"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Mais configurações"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Personalizar: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"Personalizar"</string>
<string name="notification_done" msgid="5279426047273930175">"Concluído"</string>
+ <string name="inline_undo" msgid="558916737624706010">"Desfazer"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g> do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"controles de notificação"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"opções de adiamento de notificação"</string>
@@ -725,6 +730,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Expandir"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimizar"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Fechar"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Configurações"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Arraste para baixo para dispensar"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> está em picture-in-picture"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 681d3e4..23a2f988 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -33,7 +33,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em curso"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificações"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Bateria fraca"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"A bateria está fraca. Ativar a Poupança de dados"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante. Cerca de <xliff:g id="TIME">%s</xliff:g> restante(s) com base na sua utilização."</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante. Cerca de <xliff:g id="TIME">%s</xliff:g> restante(s)."</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante. A Poupança de bateria está ativada."</string>
<string name="invalid_charger" msgid="4549105996740522523">"Carregamento USB não suportado. \nUtilize apenas o carregador fornecido."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"O carregamento por USB não é suportado."</string>
@@ -67,14 +70,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"O utilizador com sessão iniciada atualmente neste dispositivo não pode ativar a depuração USB. Para utilizar esta funcionalidade, mude para o utilizador principal."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom para preencher o ecrã"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Esticar p. caber em ec. int."</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Captura de ecrã"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"A guardar captura de ecrã..."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"A guardar captura de ecrã..."</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"A guardar captura de ecrã."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Captura de ecrã efetuada"</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Toque para ver a sua captura de ecrã."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Não foi possível obter captura de ecrã."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problema encontrado ao guardar a captura de ecrã."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Não é possível guardar a captura de ecrã devido a espaço de armazenamento limitado."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"A guardar a captura de ecrã…"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"Captura de ecrã guardada"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Toque para ver a captura de ecrã."</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"Não foi possível obter a captura de ecrã."</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Foi encontrado um problema ao guardar a captura de ecrã."</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Não é possível guardar a captura de ecrã devido a espaço de armazenamento limitado."</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"A aplicação ou a sua entidade não permitem tirar capturas de ecrã"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opções de transm. de fich. USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Montar como leitor de multimédia (MTP)"</string>
@@ -503,6 +507,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Toque para desativar o som."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Controlos de volume %s apresentados. Deslize rapidamente para cima para ignorar."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Controles de volume ocultados"</string>
+ <string name="output_title" msgid="5355078100792942802">"Saída de som multimédia"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Saída de som de chamada telefónica"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Nenhum dispositivo encontrado."</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Nenhum dispositivo encontrado. Experimente ativar o <xliff:g id="SERVICE">%1$s</xliff:g>."</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth e Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Sintonizador da interface do sistema"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Mostrar percentagem da bateria incorporada"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Mostrar a percentagem do nível da bateria no ícone da barra de estado quando não estiver a carregar"</string>
@@ -550,26 +561,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Desativado"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"Com os controlos de notificações do consumo de energia, pode definir um nível de importância de 0 a 5 para as notificações de aplicações. \n\n"<b>"Nível 5"</b>" \n- Mostrar no início da lista de notificações \n- Permitir a interrupção do ecrã inteiro \n- Aparecer rapidamente sempre \n\n"<b>"Nível 4"</b>" \n- Impedir a interrupção do ecrã inteiro \n- Aparecer rapidamente sempre\n\n"<b>"Nível 3"</b>" \n- Impedir a interrupção do ecrã inteiro \n- Nunca aparecer rapidamente \n\n"<b>"Nível 2"</b>" \n- Impedir a interrupção do ecrã inteiro \n- Nunca aparecer rapidamente \n- Nunca tocar nem vibrar \n\n"<b>"Nível 1"</b>" \n- Impedir a interrupção do ecrã inteiro \n- Nunca aparecer rapidamente \n- Nunca tocar nem vibrar \n- Ocultar do ecrã de bloqueio e da barra de estado \n- Mostrar no fim da lista de notificações \n\n"<b>"Nível 0"</b>" \n- Bloquear todas as notificações da aplicação"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notificações"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"Deixará de receber estas notificações"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> categorias de notificação"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"Esta aplicação não tem categorias de notificação"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Não é possível desativar as notificações desta aplicação"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="other">1 de <xliff:g id="NUMBER_1">%s</xliff:g> categorias de notificação desta aplicação</item>
- <item quantity="one">1 de <xliff:g id="NUMBER_0">%s</xliff:g> categoria de notificação desta aplicação</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> e mais <xliff:g id="NUMBER_5">%3$d</xliff:g></item>
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> e mais <xliff:g id="NUMBER_2">%3$d</xliff:g></item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"Nunca mais verá estas notificações."</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"Pretende continuar a ver estas notificações?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"Parar notificações"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Continuar a mostrar"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"Pretende continuar a ver notificações desta aplicação?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"Não é possível desativar estas notificações."</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Controlos de notificações da aplicação <xliff:g id="APP_NAME">%1$s</xliff:g> abertos"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Controlos de notificações da aplicação <xliff:g id="APP_NAME">%1$s</xliff:g> fechados"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Permitir notificações deste canal"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"Todas as categorias"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Mais definições"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Personalizar: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"Personalizar"</string>
<string name="notification_done" msgid="5279426047273930175">"Concluído"</string>
+ <string name="inline_undo" msgid="558916737624706010">"Anular"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g> do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"controlos de notificação"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"opções de suspensão de notificações"</string>
@@ -724,6 +728,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Expandir"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimizar"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Fechar"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Definições"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Arrastar para baixo para ignorar"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"A aplicação <xliff:g id="NAME">%s</xliff:g> está no modo de ecrã no ecrã"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 61aa901..5fb7b19 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -33,7 +33,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em andamento"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificações"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Bateria fraca"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"A bateria está com pouca carga. Ative a Economia de bateria"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> restantes"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante(s), cerca de <xliff:g id="TIME">%s</xliff:g> com base no seu uso"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante(s), cerca de <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante(s). A Economia de bateria está ativada."</string>
<string name="invalid_charger" msgid="4549105996740522523">"O carregamento via USB não é suportado.\nUse apenas o carregador fornecido."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"O carregamento via USB não é suportado."</string>
@@ -67,14 +70,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"O usuário conectado a este dispositivo não pode ativar a depuração USB. Para usar esse recurso, mude para o usuário principal \"NAME\"."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom p/ preencher a tela"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Ampliar p/ preencher tela"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Captura de tela"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Salvando captura de tela..."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Salvando captura de tela..."</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"A captura de tela está sendo salva."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Captura de tela obtida."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Toque para ver sua captura de tela."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Não foi possível obter a captura de tela."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problema encontrado ao salvar captura de tela."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Não é possível salvar a captura de tela, porque não há espaço suficiente."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"A captura de tela está sendo salva"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"Captura de tela salva"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Toque para ver sua captura de tela"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"Não foi possível fazer a captura de tela"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Problema encontrado ao salvar captura de tela"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Não é possível salvar a captura de tela, porque não há espaço suficiente"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"O app ou a organização não permitem capturas de tela"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opções transf. arq. por USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Conectar como media player (MTP)"</string>
@@ -387,7 +391,7 @@
<string name="guest_exit_guest_dialog_message" msgid="4155503224769676625">"Todos os apps e dados nesta sessão serão excluídos."</string>
<string name="guest_exit_guest_dialog_remove" msgid="7402231963862520531">"Remover"</string>
<string name="guest_wipe_session_title" msgid="6419439912885956132">"Bem-vindo, convidado."</string>
- <string name="guest_wipe_session_message" msgid="8476238178270112811">"Deseja continuar a sessão?"</string>
+ <string name="guest_wipe_session_message" msgid="8476238178270112811">"Quer continuar a sessão?"</string>
<string name="guest_wipe_session_wipe" msgid="5065558566939858884">"Recomeçar"</string>
<string name="guest_wipe_session_dontwipe" msgid="1401113462524894716">"Sim, continuar"</string>
<string name="guest_notification_title" msgid="1585278533840603063">"Usuário convidado"</string>
@@ -494,6 +498,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Multifrequência de dois tons"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Acessibilidade"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Chamadas"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"Tocar"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrar"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"Ignorar"</string>
@@ -504,6 +509,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Toque para silenciar."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s controles de volume exibidos. Deslize para cima para dispensar."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Controles de volume ocultos"</string>
+ <string name="output_title" msgid="5355078100792942802">"Saída de mídia"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Saída de chamada telefônica"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Nenhum dispositivo foi encontrado"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Nenhum dispositivo encontrado. Tente ativar o <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth e Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Sintonizador System UI"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Mostrar porcentagem de bateria incorporada"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Mostrar porcentagem de nível de bateria dentro do ícone da barra de status quando não estiver carregando"</string>
@@ -551,26 +563,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Desativado"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"Com controles de ativação de notificações, é possível definir o nível de importância de 0 a 5 para as notificações de um app. \n\n"<b>"Nível 5"</b>" \n- Exibir na parte superior da lista de notificações \n- Permitir interrupção em tela cheia \n- Sempre exibir \n\n"<b>"Nível 4"</b>" \n- Impedir interrupções em tela cheia \n- Sempre exibir \n\n"<b>"Nível 3"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n\n"<b>"Nível 2"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n- Nunca emitir som ou vibrar \n\n"<b>"Nível 1"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n- Nunca emitir som ou vibrar \n- Ocultar da tela de bloqueio e barra de status \n- Exibir na parte inferior da lista de notificações \n\n"<b>"Nível 0"</b>" \n- Bloquear todas as notificações do app"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notificações"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"Você deixará de receber essas notificações"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> categorias de notificação"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"Este app não tem categorias de notificação"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Notificações deste app não podem ser desativadas"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="one">1 de <xliff:g id="NUMBER_1">%s</xliff:g> categoria de notificação deste app</item>
- <item quantity="other">1 de <xliff:g id="NUMBER_1">%s</xliff:g> categorias de notificação deste app</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> e <xliff:g id="NUMBER_5">%3$d</xliff:g> outro</item>
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> e <xliff:g id="NUMBER_5">%3$d</xliff:g> outros</item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"Você deixará de ver essas notificações"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"Continuar mostrando essas notificações?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"Bloquear notificações"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Continuar mostrando"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuar mostrando notificações desse app?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"Não é possível desativar essas notificações"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Controles de notificação de <xliff:g id="APP_NAME">%1$s</xliff:g> abertos"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Controles de notificação de <xliff:g id="APP_NAME">%1$s</xliff:g> fechados"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Permitir notificações desse canal"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"Todas as categorias"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Mais configurações"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Personalizar: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"Personalizar"</string>
<string name="notification_done" msgid="5279426047273930175">"Concluído"</string>
+ <string name="inline_undo" msgid="558916737624706010">"Desfazer"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g> do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"controles de notificação"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"opções de adiamento de notificação"</string>
@@ -725,6 +730,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Expandir"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimizar"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Fechar"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Configurações"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Arraste para baixo para dispensar"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> está em picture-in-picture"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 2a0744e..f30f6d3 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -34,7 +34,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"În desfășurare"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificări"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Bateria este aproape descărcată"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Bateria este aproape descărcată. Activați Economisirea bateriei."</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"Procent rămas din baterie: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Procent rămas din baterie <xliff:g id="PERCENTAGE">%s</xliff:g>, în baza utilizării, timp aproximativ rămas <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Procent rămas din baterie <xliff:g id="PERCENTAGE">%s</xliff:g>, timp aproximativ rămas <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Procent rămas din baterie: <xliff:g id="PERCENTAGE">%s</xliff:g>. Economisirea bateriei este activată."</string>
<string name="invalid_charger" msgid="4549105996740522523">"Încărcarea USB nu este acceptată. \nUtilizați numai încărcătorul furnizat."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"Încărcarea prin USB nu este acceptată."</string>
@@ -68,14 +71,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Utilizatorul conectat momentan pe acest dispozitiv nu poate activa remedierea erorilor prin USB. Pentru a folosi această funcție, comutați la utilizatorul principal."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom pt. a umple ecranul"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Înt. pt. a umple ecranul"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Captură de ecran"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Se salv. captura de ecran..."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Se salvează captura de ecran..."</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Captura de ecran este salvată."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Captură de ecran salvată."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Atingeți pentru a vedea captura de ecran."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Captura de ecran nu a putut fi realizată."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problemă întâmpinată la salvarea capturii de ecran."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Captura de ecran nu poate fi salvată din cauza spațiului de stocare limitat."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"Captura de ecran este în curs de salvare"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"Captură de ecran salvată"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Atingeți pentru a vedea captura de ecran"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"Captura de ecran nu a putut fi realizată"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Problemă întâmpinată la salvarea capturii de ecran"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Captura de ecran nu poate fi salvată din cauza spațiului de stocare limitat"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Crearea capturilor de ecran nu este permisă de aplicație sau de organizația dvs."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opțiuni pentru transferul de fișiere prin USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Montați ca player media (MTP)"</string>
@@ -507,6 +511,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Atingeți pentru a dezactiva sunetul."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Comenzile de volum pentru %s sunt afișate. Glisați pentru a închide."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Comenzile de volum sunt ascunse"</string>
+ <string name="output_title" msgid="5355078100792942802">"Ieșire media"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Ieșire apel telefonic"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Nu s-a găsit niciun dispozitiv"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Nu s-au găsit dispozitive. Încercați să activați <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth și Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Afișați procentajul bateriei încorporat"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Afișați procentajul cu nivelul bateriei în interiorul pictogramei din bara de stare, atunci când nu se încarcă"</string>
@@ -554,28 +565,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Dezactivate"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"Folosind comenzile de gestionare a notificărilor, puteți să setați un nivel de importanță de la 0 la 5 pentru notificările unei aplicații. \n\n"<b>"Nivelul 5"</b>" \n– Se afișează la începutul listei de notificări \n– Se permite întreruperea pe ecranul complet \n– Se afișează întotdeauna scurt \n\n"<b>"Nivelul 4"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Se afișează întotdeauna scurt \n\n"<b>"Nivelul 3"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n\n"<b>"Nivelul 2"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n– Nu se emit sunete și nu vibrează niciodată \n\n"<b>"Nivelul 1"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n– Nu se emit sunete și nu vibrează niciodată \n– Se ascunde în ecranul de blocare și în bara de stare \n– Se afișează la finalul listei de notificări \n\n"<b>"Nivelul 0"</b>" \n– Se blochează toate notificările din aplicație"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Notificări"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"Nu veți mai primi aceste notificări"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> categorii de notificări"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"Această aplicație nu are categorii de notificare"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Notificările din această aplicație nu pot fi dezactivate"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="few">1 din <xliff:g id="NUMBER_1">%s</xliff:g> categorii de notificare din această aplicație</item>
- <item quantity="other">1 din <xliff:g id="NUMBER_1">%s</xliff:g> de categorii de notificare din această aplicație</item>
- <item quantity="one">1 din <xliff:g id="NUMBER_0">%s</xliff:g> categorie de notificare din această aplicație</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="few"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, și încă <xliff:g id="NUMBER_5">%3$d</xliff:g></item>
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, și încă <xliff:g id="NUMBER_5">%3$d</xliff:g></item>
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>, și încă <xliff:g id="NUMBER_2">%3$d</xliff:g></item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"Nu veți mai vedea aceste notificări"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"Doriți să continuați afișarea acestor notificări?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"Opriți notificările"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Continuați afișarea"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"Doriți să continuați afișarea notificărilor de la această aplicație?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"Aceste notificări nu pot fi dezactivate"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Opțiunile privind notificările pentru <xliff:g id="APP_NAME">%1$s</xliff:g> sunt afișate"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Opțiunile privind notificările pentru <xliff:g id="APP_NAME">%1$s</xliff:g> nu sunt afișate"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Permiteți notificările de la acest canal"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"Toate categoriile"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Mai multe setări"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Personalizați: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"Personalizați"</string>
<string name="notification_done" msgid="5279426047273930175">"Terminat"</string>
+ <string name="inline_undo" msgid="558916737624706010">"Anulați"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"comenzile notificării"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"opțiuni de amânare a notificării"</string>
@@ -732,6 +734,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Extindeți"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimizați"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Închideți"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Setări"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Trageți în jos pentru a închide"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Meniu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> este în modul picture-in-picture"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index ac4f5fe..4be72ed 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -35,7 +35,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Текущие"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Уведомления"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Батарея почти разряжена"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Батарея почти разряжена. Включите режим энергосбережения."</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"Осталось: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Заряд батареи – <xliff:g id="PERCENTAGE">%s</xliff:g>, осталось примерно <xliff:g id="TIME">%s</xliff:g> при текущем уровне использования."</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Заряд батареи – <xliff:g id="PERCENTAGE">%s</xliff:g>, осталось примерно <xliff:g id="TIME">%s</xliff:g>."</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Уровень заряда батареи: <xliff:g id="PERCENTAGE">%s</xliff:g>. Включен режим энергосбережения."</string>
<string name="invalid_charger" msgid="4549105996740522523">"Зарядка через порт USB не поддерживается.\nИспользуйте только зарядное устройство из комплекта поставки."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"Зарядка через USB не поддерживается."</string>
@@ -69,14 +72,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"В этом аккаунте нельзя включить отладку по USB. Перейдите в аккаунт основного пользователя."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Подогнать по размерам экрана"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Растянуть на весь экран"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Скриншот"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Сохранение..."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Сохранение..."</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Сохранение..."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Скриншот сохранен"</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Нажмите, чтобы увидеть скриншот"</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Не удалось сохранить скриншот."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Не удалось сохранить скриншот."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Не удалось сохранить скриншот: недостаточно места."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"Сохранение…"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"Скриншот сохранен"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Нажмите, чтобы увидеть скриншот."</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"Ошибка при сохранении скриншота"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Не удалось сохранить скриншот."</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Не удалось сохранить скриншот: недостаточно места."</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Не удалось сделать скриншот: нет разрешения от приложения или организации."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Параметры передачи через USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Подключить как мультимедийный проигрыватель (MTP)"</string>
@@ -498,6 +502,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Тональный набор"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Специальные возможности"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Вызовы"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"Со звуком"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Вибрация"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"Без звука"</string>
@@ -508,6 +513,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Нажмите, чтобы выключить звук."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Показаны регуляторы громкости: %s. Проведите вверх, чтобы скрыть."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Регуляторы громкости скрыты"</string>
+ <string name="output_title" msgid="5355078100792942802">"Выход мультимедиа"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Выход телефонных вызовов"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Устройств не найдено"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Устройств не найдено. Включите <xliff:g id="SERVICE">%1$s</xliff:g>."</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth и Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Показывать уровень заряда батареи в процентах"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Когда устройство работает в автономном режиме, процент заряда батареи показан в строке состояния"</string>
@@ -555,30 +567,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Отключено"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"С помощью этой функции вы можете устанавливать уровень важности уведомлений от 0 до 5 для каждого приложения.\n\n"<b>"Уровень 5"</b>\n"‒ Помещать уведомления в начало списка.\n‒ Показывать полноэкранные уведомления.\n‒ Показывать всплывающие уведомления.\nУровень 4\n"<b></b>\n"‒ Не показывать полноэкранные уведомления.\n‒ Показывать всплывающие уведомления.\nУровень 3\n"<b></b>\n"‒ Не показывать полноэкранные уведомления.\n‒ Не показывать всплывающие уведомления.\nУровень 2\n"<b></b>\n"‒ Не показывать полноэкранные уведомления.\n‒ Не показывать всплывающие уведомления.\n‒ Не использовать звук и вибрацию.\nУровень 1\n"<b></b>\n"‒ Не показывать полноэкранные уведомления.\n‒ Не показывать всплывающие уведомления.\n‒ Не использовать звук и вибрацию.\n‒ Не показывать на экране блокировки и в строке состояния.\n‒ Помещать уведомления в конец списка.\nУровень 0\n"<b></b>\n"‒ Блокировать все уведомления приложения."</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Уведомления"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"Вы больше не будете получать эти уведомления"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"Категорий уведомлений: <xliff:g id="NUMBER">%d</xliff:g>"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"В приложении нет категорий уведомлений"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Уведомления этого приложения нельзя отключить"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="one">1 из <xliff:g id="NUMBER_1">%s</xliff:g> категории уведомлений этого приложения</item>
- <item quantity="few">1 из <xliff:g id="NUMBER_1">%s</xliff:g> категорий уведомлений этого приложения</item>
- <item quantity="many">1 из <xliff:g id="NUMBER_1">%s</xliff:g> категорий уведомлений этого приложения</item>
- <item quantity="other">1 из <xliff:g id="NUMBER_1">%s</xliff:g> категории уведомлений этого приложения</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> и ещё <xliff:g id="NUMBER_5">%3$d</xliff:g> канал</item>
- <item quantity="few"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> и ещё <xliff:g id="NUMBER_5">%3$d</xliff:g> канала</item>
- <item quantity="many"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> и ещё <xliff:g id="NUMBER_5">%3$d</xliff:g> каналов</item>
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> и ещё <xliff:g id="NUMBER_5">%3$d</xliff:g> канала</item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"Вы больше не будете получать эти уведомления."</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"Показывать эти уведомления?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"Отключить уведомления"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Показывать"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"Показывать уведомления от этого приложения?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"Эти уведомления нельзя отключить."</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Настройки уведомлений для приложения <xliff:g id="APP_NAME">%1$s</xliff:g> открыты"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Настройки уведомлений для приложения <xliff:g id="APP_NAME">%1$s</xliff:g> закрыты"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Показывать уведомления с этого канала"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"Все категории"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Другие настройки"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"<xliff:g id="SUB_CATEGORY">%1$s</xliff:g>: настроить"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"Настроить"</string>
<string name="notification_done" msgid="5279426047273930175">"Готово"</string>
+ <string name="inline_undo" msgid="558916737624706010">"Отменить"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g>: <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"настройки уведомлений"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"параметры отсрочки уведомлений"</string>
@@ -737,6 +738,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Развернуть"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Свернуть"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Закрыть"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Настройки"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Чтобы закрыть, потяните вниз"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Меню"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> находится в режиме \"Картинка в картинке\""</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 12f4838..f072b73 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -33,7 +33,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"දැනට පවතින"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"දැනුම්දීම්"</string>
<string name="battery_low_title" msgid="6456385927409742437">"බැටරිය අඩුය"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"බැටරිය අඩුයි. බැටරි සුරැකුම ක්රියාත්මක කරන්න"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> ඉතිරිව තිබේ"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> ඉතිරිව ඇත, ඔබගේ භාවිතය මත පදනම්ව <xliff:g id="TIME">%s</xliff:g> පමණ"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> ඉතිරිව ඇත, <xliff:g id="TIME">%s</xliff:g> පමණ ඇත"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> ඉතිරිව ඇත. බැටරි සුරැකුම ක්රියාත්මකයි."</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB ආරෝපණය සහය නොදක්වයි.\nසපයන ලද ආරෝපකය පමණක් භාවිතා කරන්න."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"USB ආරෝපණය කිරීම සහාය නොදක්වයි."</string>
@@ -67,14 +70,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"දැනට මෙම උපාංගයට පුරා ඇති පරිශීලකයාට USB නිදොස්කරණය ක්රියාත්මක කළ නොහැක. මෙම විශේෂාංගය භාවිතා කිරීම සඳහා, මූලික පරිශීලකයා වෙත මාරු වෙන්න."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"තිරය පිරවීමට විශාලනය කරන්න"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"තිරය පිරවීමට අදින්න"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"තිර රුව"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"තිර රුව සුරකිමින්…"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"තිර රුව සුරැකෙමින් පවතී…"</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"තිර රුව සුරැකෙමින් පවතී."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"තිර රුව ග්රහණය කරන ලදි."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"ඔබගේ තිර රුව බැලීමට තට්ටු කරන්න."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"තිර රුව ග්රහණය කිරීමට නොහැකි විය."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"තිර රුව සුරකින අතරතුර ගැටලුවක් ඇති විය."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"සීමිත ගබඩා ඉඩ නිසා තිර රුව සුරැකිය නොහැකිය."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"තිර රුව සුරකිමින් පවතී"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"තිර රුව සුරකින ලදී"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"ඔබගේ තිර රුව බැලීමට තට්ටු කරන්න"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"තිර රුව ග්රහණය කිරීමට නොහැකි විය"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"තිර රුව සුරකින අතරතුර ගැටලුවක් ඇති විය"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"සීමිත ගබඩා ඉඩ නිසා තිර රුව සුරැකිය නොහැකිය"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"තිර රූ ගැනීමට යෙදුම හෝ ඔබගේ සංවිධානය ඉඩ නොදේ"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB ගොනු හුවමාරු විකල්ප"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"මධ්ය ධාවකයක් (MTP) ලෙස සවි කරන්න"</string>
@@ -492,6 +496,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"බ්ලූටූත්"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"ද්විත්ව බහු ස්වර සංඛ්යාතය"</string>
<string name="stream_accessibility" msgid="301136219144385106">"ප්රවේශ්යතාව"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"ඇමතුම්"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"නාද කරන්න"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"කම්පනය කරන්න"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"නිහඬ කරන්න"</string>
@@ -502,6 +507,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. නිහඬ කිරීමට තට්ටු කරන්න."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s හඬ පරිමා පාලන පෙන්වයි. ඉවත දැමීමට ස්වයිප් කරන්න."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"හඩ පරිමා පාලන සඟවා ඇත"</string>
+ <string name="output_title" msgid="5355078100792942802">"මාධ්ය ප්රතිදානය"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"දුරකථන ඇමතුම් ප්රතිදානය"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"උපාංග හමු නොවිණි"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"උපාංග හමු නොවිණි. <xliff:g id="SERVICE">%1$s</xliff:g> ක්රියාත්මක කිරීම උත්සාහ කරන්න"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"බ්ලූටූත්"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"බ්ලූටූත් සහ Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"පද්ධති UI සුසරකය"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"කාවද්දන ලද බැටරි ප්රතිශතය පෙන්වන්න"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"ආරෝපණය නොවන විට තත්ත්ව තීරු නිරූපකය ඇතුළත බැටරි මට්ටම් ප්රතිශතය පෙන්වන්න"</string>
@@ -549,26 +561,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ක්රියාවිරහිතයි"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"බල දැනුම්දීම් පාලන සමගින්, ඔබට යෙදුමක දැනුම්දීම් සඳහා වැදගත්කම 0 සිට 5 දක්වා සැකසිය හැකිය. \n\n"<b>"5 මට්ටම"</b>" \n- දැනුම්දීම් ලැයිස්තුවේ ඉහළින්ම පෙන්වන්න \n- පූර්ණ තිර බාධාවට ඉඩ දෙන්න \n- සැම විට එබී බලන්න \n\n"<b>"4 මට්ටම"</b>" \n- පූර්ණ තිර බාධාව වළක්වන්න \n- සැම විට එබී බලන්න \n\n"<b>"3 මට්ටම"</b>" \n- පූර්ණ තිර බාධාව වළක්වන්න \n- කිසි විටක එබී නොබලන්න \n\n"<b>"2 මට්ටම"</b>" \n- පූර්ණ තිර බාධාව වළක්වන්න \n- කිසි විටක එබී නොබලන්න \n- කිසි විටක හඬ සහ කම්පනය සිදු නොකරන්න \n\n"<b>"1 මට්ටම"</b>" \n- පූර්ණ තිර බාධාව වළක්වන්න \n- කිසි විටක එබී නොබලන්න \n- කිසි විටක හඬ සහ කම්පනය සිදු නොකරන්න \n- අගුලු තිරය සහ තත්ත්ව තීරුව වෙතින් සඟවන්න \n- දැනුම්දීම් ලැයිස්තුවේ පහළින්ම පෙන්වන්න \n\n"<b>"0 මට්ටම"</b>" \n- යෙදුම වෙතින් වන සියලු දැනුම් දීම් සඟවන්න."</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"දැනුම් දීම්"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"ඔබට තවදුරටත් මෙම දැනුම්දීම් නොලැබෙනු ඇත"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"දැනුම්දීම් කාණ්ඩ <xliff:g id="NUMBER">%d</xliff:g>ක්"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"මෙම යෙදුම හට දැනුම්දීම් ප්රවර්ග නොමැත"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"මෙම යෙදුම වෙතින් වන දැනුම්දීම් ක්රියාවිරහිත කළ නොහැකිය"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="one">මෙම යෙදුමෙන් දැනුම්දීම් ප්රවර්ග <xliff:g id="NUMBER_1">%s</xliff:g>න් 1ක්</item>
- <item quantity="other">මෙම යෙදුමෙන් දැනුම්දීම් ප්රවර්ග <xliff:g id="NUMBER_1">%s</xliff:g>න් 1ක්</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, සහ තවත් <xliff:g id="NUMBER_5">%3$d</xliff:g>ක්</item>
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, සහ තවත් <xliff:g id="NUMBER_5">%3$d</xliff:g>ක්</item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"ඔබට තවදුරටත් මෙම දැනුම්දීම් නොදකිනු ඇත"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"මෙම දැනුම්දීම් පෙන්වමින් තබන්නද?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"දැනුම්දීම් නවත්වන්න"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"පෙන්වමින් තබන්න"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"මෙම යෙදුම වෙතින් දැනුම්දීම් පෙන්වමින් තබන්නද?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"මෙම දැනුම්දීම් ක්රියාවිරහිත කළ නොහැකිය"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා දැනුම්දීම් පාලන විවෘත කරන ලදී"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා දැනුම්දීම් පාලන වසන ලදී"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"මෙම නාලිකාව වෙතින් දැනුම්දීම් සඳහා ඉඩ දෙන්න"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"සියලු ප්රවර්ග"</string>
<string name="notification_more_settings" msgid="816306283396553571">"තව සැකසීම්"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"අභිමත කරන්න: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"අභිරුචිකරණය"</string>
<string name="notification_done" msgid="5279426047273930175">"නිමයි"</string>
+ <string name="inline_undo" msgid="558916737624706010">"පසුගමනය කරන්න"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"දැනුම්දීම් පාලන"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"දැනුම්දීම් මදක් නතර කිරීමේ විකල්ප"</string>
@@ -723,6 +728,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"දිග හරින්න"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"කුඩා කරන්න"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"වසන්න"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"සැකසීම්"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"ඉවත ලෑමට පහළට ඇදගෙන යන්න"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"මෙනුව"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> පින්තූරය-තුළ-පින්තූරය තුළ වේ"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 694695c..49415a5 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -509,6 +509,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Klepnutím vypnete zvuk."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Zobrazujú sa ovládacie prvky hlasitosti zariadenia %s. Prejdením prstom nahor to odmietnete."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Ovládacie prvky hlasitosti sú skryté"</string>
+ <string name="output_title" msgid="5355078100792942802">"Výstup médií"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Výstup telefonického hovoru"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Nenašli sa žiadne zariadenia"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Nenašli sa žiadne zariadenia. Skúste zapnúť službu <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth a Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Tuner používateľského rozhrania systému"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Zobraziť percentá vloženej batérie"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Percentuálne zobrazenie nabitia batérie vnútri ikony v stavovom riadku, keď neprebieha nabíjanie"</string>
@@ -738,6 +745,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Rozbaliť"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimalizovať"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Zavrieť"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Zrušíte presunutím nadol"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Ponuka"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> je v režime obraz v obraze"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 10d0104..e52a43a 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -35,7 +35,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Trenutno"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Obvestila"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Akumulator je skoraj izpraznjen"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Akumulator je skoraj izpraznjen. Vklopite varčevanje z energijo akumulatorja"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"Še <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Še <xliff:g id="PERCENTAGE">%s</xliff:g>, glede na način uporabe imate na voljo še približno <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Še <xliff:g id="PERCENTAGE">%s</xliff:g>, na voljo imate še približno <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Še <xliff:g id="PERCENTAGE">%s</xliff:g>. Vklopljeno je varčevanje z energijo akumulatorja."</string>
<string name="invalid_charger" msgid="4549105996740522523">"Polnjenje po povezavi USB ni podprto.\nUporabite priloženi polnilnik."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"Polnjenje prek USB-ja ni podprto."</string>
@@ -69,14 +72,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Uporabnik, trenutno prijavljen v napravo, ne more vklopiti odpravljanja napak s povezavo USB. Če želite uporabljati to funkcijo, preklopite na primarnega uporabnika."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Povečava čez cel zaslon"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Raztegnitev čez zaslon"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Posnetek zaslona"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Shranjev. posnetka zaslona ..."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Shranjevanje posnetka zaslona ..."</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Shranjevanje posnetka zaslona."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Posnetek zaslona je shranjen."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Dotaknite se, če si želite ogledati posnetek zaslona."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Posnetka zaslona ni bilo mogoče shraniti."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Pri shranjevanju posnetka zaslona je prišlo do težave."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Shranjevanje posnetka zaslona ni mogoče zaradi omejenega prostora za shranjevanje."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"Shranjevanje posnetka zaslona"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"Posnetek zaslona je shranjen"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Dotaknite se, če si želite ogledati posnetek zaslona"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"Posnetka zaslona ni bilo mogoče shraniti"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Pri shranjevanju posnetka zaslona je prišlo do težave"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Shranjevanje posnetka zaslona ni mogoče zaradi omejenega prostora za shranjevanje"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Aplikacija ali vaša organizacija ne dovoljuje posnetkov zaslona"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Možnosti prenosa datotek prek USB-ja"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Vpni kot predvajalnik (MTP)"</string>
@@ -509,6 +513,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Dotaknite se, če želite izklopiti zvok."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Prikazani so ti kontrolniki za glasnost: %s. Povlecite navzgor za opustitev."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Kontrolniki za glasnost so skriti."</string>
+ <string name="output_title" msgid="5355078100792942802">"Izhod predstavnosti"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Izhod telefonskih klicev"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Ni naprav"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Ni naprav. Poskusite vklopiti <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth in Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Uglaševalnik uporabniškega vmesnika sistema"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Prikaži odstotek napolnjenosti vgraj. akumulatorja"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Prikaz odstotka napolnjenosti akumulatorja znotraj ikone v vrstici stanja, ko se ne polni"</string>
@@ -556,30 +567,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Izklopljeno"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"S kontrolniki za pomebnost obvestila je mogoče za obvestila aplikacije nastaviti stopnjo pomembnosti od 0 do 5. \n\n"<b>"Stopnja 5"</b>" \n– Prikaz na vrhu seznama obvestil \n– Omogočanje prekinitev v celozaslonskem načinu \n– Vedno prikaži hitre predoglede \n\n"<b>"Stopnja 4"</b>" \n– Preprečevanje prekinitev v celozaslonskem načinu \n– Vedno prikaži hitre predoglede \n\n"<b>"Stopnja 3"</b>" \n– Preprečevanje prekinitev v celozaslonskem načinu \n– Nikoli ne prikaži hitrih predogledov \n\n"<b>"Stopnja 2"</b>" \n– Preprečevanje prekinitev v celozaslonskem načinu \n– Nikoli ne prikaži hitrih predogledov \n– Nikoli ne uporabi zvoka in vibriranja \n\n"<b>"Stopnja 1"</b>" \n– Preprečevanje prekinitev v celozaslonskem načinu \n– Nikoli ne prikaži hitrih predogledov \n– Nikoli ne uporabi zvoka in vibriranja \n– Skrivanje na zaklenjenem zaslonu in v vrstici stanja \n– Prikaz na dnu seznama obvestil \n\n"<b>"Stopnja 0"</b>" \n– Blokiranje vseh obvestil aplikacije"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Obvestila"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"Teh obvestil ne boste več prejemali"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"Št. kategorij obvestil: <xliff:g id="NUMBER">%d</xliff:g>"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"Ta aplikacija nima kategorij obvestil"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Obvestil te aplikacije ni mogoče izklopiti"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="one">1 od <xliff:g id="NUMBER_1">%s</xliff:g> kategorije obvestil iz te aplikacije</item>
- <item quantity="two">1 od <xliff:g id="NUMBER_1">%s</xliff:g> kategorij obvestil iz te aplikacije</item>
- <item quantity="few">1 od <xliff:g id="NUMBER_1">%s</xliff:g> kategorij obvestil iz te aplikacije</item>
- <item quantity="other">1 od <xliff:g id="NUMBER_1">%s</xliff:g> kategorij obvestil iz te aplikacije</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> in <xliff:g id="NUMBER_5">%3$d</xliff:g> drug</item>
- <item quantity="two"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> in <xliff:g id="NUMBER_5">%3$d</xliff:g> druga</item>
- <item quantity="few"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> in <xliff:g id="NUMBER_5">%3$d</xliff:g> drugi</item>
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> in <xliff:g id="NUMBER_5">%3$d</xliff:g> drugih</item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"Ta obvestila ne bodo več prikazana"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"Želite, da so ta obvestila še naprej prikazana?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"Ustavi prikazovanje obvestil"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Prikazuj še naprej"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"Želite, da so obvestila te aplikacije še naprej prikazana?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"Teh obvestil ni mogoče izklopiti"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Kontrolniki obvestil za aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g> so odprti"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Kontrolniki obvestil za aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g> so zaprti"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Dovoli obvestila iz tega kanala"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"Vse kategorije"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Več nastavitev"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Prilagodi: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"Prilagodi"</string>
<string name="notification_done" msgid="5279426047273930175">"Dokončano"</string>
+ <string name="inline_undo" msgid="558916737624706010">"Razveljavi"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"kontrolniki obvestil"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"možnosti preložitve obvestil"</string>
@@ -738,6 +738,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Razširi"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimiraj"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Zapri"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Nastavitve"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Povlecite navzdol, da opustite"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Meni"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> je v načinu slika v sliki"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 665177f..2d1591f 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Trokit për ta çaktivizuar."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Tregohen %s kontrolle volumi. Rrëshqit lart për ta larguar."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Kontrollet e volumit janë fshehur"</string>
+ <string name="output_title" msgid="5355078100792942802">"Dalja e pajisjes"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Dalja e telefonatës"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Nuk u gjet asnjë pajisje"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Nuk u gjet asnjë pajisje. Provo të aktivizosh <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth dhe Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Sintonizuesi i Sistemit të Ndërfaqes së Përdoruesit"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Shfaq përqindjen e baterisë së integruar"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Shfaq përqindjen e nivelit të baterisë brenda ikonës së shiritit të statusit kur nuk është duke u ngarkuar."</string>
@@ -724,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Zgjero"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimizo"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Mbyll"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Zvarrit poshtë për të larguar"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menyja"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> është në figurë brenda figurës"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index cef0f69..99d8008 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -505,6 +505,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Додирните да бисте искључили звук."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Контроле за јачину звука (%s) су приказане. Превуците нагоре да бисте их одбацили."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Контроле за јачину звука су сакривене"</string>
+ <string name="output_title" msgid="5355078100792942802">"Излаз медија"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Излаз за телефонски позив"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Није пронађен ниједан уређај"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Није пронађен ниједан уређај. Пробајте да укључите услугу <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth и Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Тјунер за кориснички интерфејс система"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Приказуј уграђени проценат батерије"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Приказивање нивоа напуњености батерије у процентима унутар иконе на статусној траци када се батерија не пуни"</string>
@@ -730,6 +737,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Прошири"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Умањи"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Затвори"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Превуците надоле да бисте одбили"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Мени"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> је слика у слици"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index c8f8db4..2d267df 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Tryck här om du vill stänga av ljudet."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Volymkontrollerna för %s visas. Svep uppåt för att ignorera."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Volymkontrollerna är dolda"</string>
+ <string name="output_title" msgid="5355078100792942802">"Medieuppspelning"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Utdata för samtal"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Inga enheter hittades"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Inga enheter hittades. Testa att aktivera <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth och Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Inställningar för systemgränssnitt"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Visa inbäddad batteriprocent"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Visa batterinivå i procent i statusfältsikonen när enheten inte laddas"</string>
@@ -724,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Utöka"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Minimera"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Stäng"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Tryck och dra nedåt för att avvisa"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Meny"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> visas i bild-i-bild"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 8fa370d..58eac29 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -33,7 +33,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Inaendelea"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Arifa"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Betri inaisha"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Chaji imepungua. Washa Kiokoa Betri"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"Imebakisha <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Imesalia <xliff:g id="PERCENTAGE">%s</xliff:g>, zimesalia takribani <xliff:g id="TIME">%s</xliff:g> kulingana na jinsi unavyotumia"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Imesalia <xliff:g id="PERCENTAGE">%s</xliff:g>, zimesalia takribani <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Imesalia <xliff:g id="PERCENTAGE">%s</xliff:g>. Umewasha Kiokoa Betri."</string>
<string name="invalid_charger" msgid="4549105996740522523">"Chaji ya USB haihamiliwi.\n Tumia chaka iliyopeanwa."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"Kuchaji kwa kutumia USB hakutumiki."</string>
@@ -67,19 +70,20 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Mtumiaji aliyeingia katika akaunti kwa kutumia kifaa hiki kwa sasa hawezi kuwasha utatuzi wa USB. Ili utumie kipengele hiki, tumia akaunti ya mtumiaji wa msingi."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Kuza ili kujaza skrini"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Tanua ili kujaza skrini"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Picha ya skrini"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Inahifadhi picha ya skrini..."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Inahifadhi picha ya skrini..."</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Picha ya skrini inahifadhiwa"</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Picha ya skrini imenaswa."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Gusa ili utazame picha ya skrini uliyohifadhi."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Haikuweza kunasa picha ya skrini"</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Hitilafu imetokea wakati wa kuhifadhi picha ya skrini."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Haina nafasi ya kutosha kuhifadhi picha ya skrini."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"Inahifadhi picha ya skrini"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"Imehifadhi picha ya skrini"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Gusa ili utazame picha ya skrini uliyohifadhi"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"Imeshindwa kupiga picha ya skrini"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Hitilafu imetokea wakati wa kuhifadhi picha ya skrini"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Imeshindwa kuhifadhi picha ya skrini kwa sababu nafasi haitoshi"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Programu au shirika lako halikuruhusu kupiga picha za skrini"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Machaguo ya uhamisho wa faili la USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Angika kama kichezaji cha maudhui (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Angika kama kamera (PTP)"</string>
- <string name="installer_cd_button_title" msgid="2312667578562201583">"Sakinisha programu ya Kuhamisha Faili ya Android ya Mac"</string>
+ <string name="installer_cd_button_title" msgid="2312667578562201583">"Weka programu ya Kuhamisha Faili inayotumika kwenye Mac"</string>
<string name="accessibility_back" msgid="567011538994429120">"Nyuma"</string>
<string name="accessibility_home" msgid="8217216074895377641">"Nyumbani"</string>
<string name="accessibility_menu" msgid="316839303324695949">"Menyu"</string>
@@ -503,6 +507,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Gusa ili usitishe."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Inaonyesha %s ya vidhibiti vya sauti. Telezesha kidole juu ili uondoe."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Imeficha vidhibiti vya sauti"</string>
+ <string name="output_title" msgid="5355078100792942802">"Vifaa vya kutoa maudhui"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Vifaa vya kutoa sauti ya simu"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Hakuna vifaa vilivyopatikana"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Hakuna vifaa vilivyopatikana. Jaribu kuwasha <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth na Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Kirekebishi cha kiolesura cha mfumo"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Onyesha asilimia ya betri iliyopachikwa"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Onyesha asilimia ya kiwango cha betri ndani ya aikoni ya sehemu ya arifa inapokuwa haichaji"</string>
@@ -550,26 +561,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Imezimwa"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"Ukiwa na udhibiti wa arifa, unaweza kuweka kiwango cha umuhimu wa arifa za programu kuanzia 0 hadi 5. \n\n"<b>"Kiwango cha 5"</b>" \n- Onyesha katika sehemu ya juu ya orodha ya arifa \n- Ruhusu ukatizaji wa skrini nzima \n- Ruhusu arifa za kuchungulia kila wakati\n\n"<b>"Kiwango cha 4"</b>" \n- Zuia ukatizaji wa skrini nzima\n- Ruhusu arifa za kuchungulia kila wakati \n\n"<b>"Kiwango cha 3"</b>" \n- Zuia ukatizaji wa skrini nzima\n- Usiruhusu kamwe arifa za kuchungulia\n\n"<b>"Kiwango cha 2"</b>" \n- Zuia ukatizaji wa skrini nzima\n- Usiruhusu kamwe arifa za kuchungulia \n- Usiruhusu kamwe sauti au mtetemo \n\n"<b>"Kiwango cha 1"</b>" \n- Zuia ukatizaji wa skrini nzima \n- Usiruhusu kamwe arifa za kuchungulia \n- Usiruhusu kamwe sauti na mtetemo \n- Usionyeshe skrini iliyofungwa na sehemu ya arifa \n- Onyesha katika sehemu ya chini ya orodha ya arifa \n\n"<b>"Kiwango cha 0"</b>" \n- Zuia arifa zote kutoka programu"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Arifa"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"Hutapokea arifa hizi tena"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"Aina <xliff:g id="NUMBER">%d</xliff:g> za arifa"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"Programu hii haina aina za arifa"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Arifa kutoka kwenye programu hii haziwezi kuzimwa"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="other">Aina 1 ya arifa kati ya <xliff:g id="NUMBER_1">%s</xliff:g> kutoka kwenye kifaa hiki</item>
- <item quantity="one">Aina 1 ya arifa kati ya <xliff:g id="NUMBER_0">%s</xliff:g> kutoka kwenye kifaa hiki</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> na vingine <xliff:g id="NUMBER_5">%3$d</xliff:g></item>
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> na kingine <xliff:g id="NUMBER_2">%3$d</xliff:g></item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"Hutaona tena arifa hizi"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"Ungependa kuendelea kuonyesha arifa hizi?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"Acha kuonyesha arifa"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Endelea kuonyesha"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"Ungependa kuendelea kuonyesha arifa kutoka programu hii?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"Huwezi kuzima arifa hizi"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Vidhibiti vya arifa <xliff:g id="APP_NAME">%1$s</xliff:g> vimefunguliwa"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Vidhibiti vya arifa vya <xliff:g id="APP_NAME">%1$s</xliff:g> vimefungwa"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Ruhusu arifa kutoka kwenye kituo hiki"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"Aina Zote"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Mipangilio zaidi"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Badilisha upendavyo: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"Weka mapendeleo"</string>
<string name="notification_done" msgid="5279426047273930175">"Nimemaliza"</string>
+ <string name="inline_undo" msgid="558916737624706010">"Tendua"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"vidhibiti vya arifa"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"chaguo za kuahirisha arifa"</string>
@@ -724,6 +728,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Panua"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Punguza"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Funga"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Mipangilio"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Buruta ili uondoe"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menyu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> iko katika hali ya picha ndani ya picha nyingine"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 6568381..544cd79 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -492,8 +492,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"புளூடூத்"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"டூயல் டோன் மல்டி ஃப்ரீக்வென்சி"</string>
<string name="stream_accessibility" msgid="301136219144385106">"அணுகல்தன்மை"</string>
- <!-- no translation found for ring_toggle_title (3281244519428819576) -->
- <skip />
+ <string name="ring_toggle_title" msgid="3281244519428819576">"அழைப்புகள்"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"ஒலி"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"அதிர்வு"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"அமைதி"</string>
@@ -504,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. ஒலியடக்க, தட்டவும்."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s ஒலிக் கட்டுப்பாடுகள் காட்டப்பட்டன. நிராகரிக்க, மேலே ஸ்வைப் செய்யவும்."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"ஒலிக் கட்டுப்பாடுகள் மறைக்கப்பட்டன"</string>
+ <string name="output_title" msgid="5355078100792942802">"மீடியா வெளியீடு"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"ஃபோன் அழைப்பு வெளியீடு"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"சாதனங்கள் எதுவும் இல்லை"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"சாதனங்கள் எதுவும் இல்லை. <xliff:g id="SERVICE">%1$s</xliff:g>ஐ ஆன் செய்யவும்"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"புளூடூத்"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"வைஃபை"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"புளூடூத் மற்றும் வைஃபை"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"உள்ளிணைந்த பேட்டரி சதவீதத்தைக் காட்டு"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"சார்ஜ் செய்யாத போது, நிலைப் பட்டி ஐகானின் உள்ளே பேட்டரி அளவு சதவீதத்தைக் காட்டும்"</string>
@@ -725,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"விரி"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"சிறிதாக்கு"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"மூடு"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"நிராகரிக்க, கீழே இழுக்கவும்"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"மெனு"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> தற்போது பிக்ச்சர்-இன்-பிக்ச்சரில் உள்ளது"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index fa7cb09..f8a14d0 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -33,7 +33,13 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"కొనసాగుతున్నవి"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"నోటిఫికేషన్లు"</string>
<string name="battery_low_title" msgid="6456385927409742437">"బ్యాటరీ తక్కువగా ఉంది"</string>
+ <!-- no translation found for battery_low_title_hybrid (6268991275887381595) -->
+ <skip />
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> మిగిలి ఉంది"</string>
+ <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) -->
+ <skip />
+ <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) -->
+ <skip />
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> మిగిలి ఉంది. బ్యాటరీ సేవర్ ఆన్లో ఉంది."</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB ఛార్జింగ్కు మద్దతు లేదు.\nఅందించిన ఛార్జర్ను మాత్రమే ఉపయోగించండి."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"USB ఛార్జింగ్కి మద్దతు లేదు."</string>
@@ -67,14 +73,22 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"ఈ పరికరానికి ప్రస్తుతం సైన్ ఇన్ చేసిన వినియోగదారు USB డీబగ్గింగ్ ఆన్ చేయలేరు. ఈ ఫీచర్ ఉపయోగించడానికి, ప్రాథమిక వినియోగదారుకి మారాలి."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"స్క్రీన్కు నింపేలా జూమ్ చేయండి"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"స్క్రీన్కు నింపేలా విస్తరించండి"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"స్క్రీన్షాట్ను సేవ్ చేస్తోంది…"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"స్క్రీన్షాట్ను సేవ్ చేస్తోంది…"</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"స్క్రీన్షాట్ సేవ్ చేయబడుతోంది."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"స్క్రీన్షాట్ క్యాప్చర్ చేయబడింది."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"మీ స్క్రీన్షాట్ను వీక్షించడానికి నొక్కండి."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"స్క్రీన్షాట్ను క్యాప్చర్ చేయడం సాధ్యపడలేదు."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"స్క్రీన్షాట్ని సేవ్ చేస్తున్నప్పుడు సమస్య సంభవించింది."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"పరిమిత నిల్వ స్థలం కారణంగా స్క్రీన్షాట్ను సేవ్ చేయడం సాధ్యపడదు."</string>
+ <!-- no translation found for screenshot_saving_text (2545047868936087248) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_title (5637073968117370753) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_text (7574667448002050363) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_title (9096484883063264803) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) -->
+ <skip />
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"స్క్రీన్షాట్లు తీయడానికి యాప్ లేదా మీ సంస్థ అనుమతించలేదు"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB ఫైల్ బదిలీ ఎంపికలు"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"మీడియా ప్లేయర్గా (MTP) మౌంట్ చేయి"</string>
@@ -492,8 +506,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"బ్లూటూత్"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"డ్యూయల్ మల్టీ టోన్ ఫ్రీక్వెన్సీ"</string>
<string name="stream_accessibility" msgid="301136219144385106">"యాక్సెస్ సామర్థ్యం"</string>
- <!-- no translation found for ring_toggle_title (3281244519428819576) -->
- <skip />
+ <string name="ring_toggle_title" msgid="3281244519428819576">"కాల్లు"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"రింగ్"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"వైబ్రేట్"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"మ్యూట్"</string>
@@ -504,6 +517,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. మ్యూట్ చేయడానికి నొక్కండి."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s వాల్యూమ్ నియంత్రణలు చూపబడ్డాయి. తీసివేయడానికి పైకి స్వైప్ చేయండి."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"వాల్యూమ్ నియంత్రణలు దాచబడ్డాయి"</string>
+ <string name="output_title" msgid="5355078100792942802">"మీడియా అవుట్పుట్"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"ఫోన్ కాల్ అవుట్పుట్"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"పరికరాలు ఏవీ కనుగొనబడలేదు"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"పరికరాలు ఏవీ కనుగొనబడలేదు. <xliff:g id="SERVICE">%1$s</xliff:g>ని ఆన్ చేసి ప్రయత్నించండి"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"బ్లూటూత్"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"బ్లూటూత్ మరియు Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"సిస్టమ్ UI ట్యూనర్"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"పొందుపరిచిన బ్యాటరీ శాతం చూపు"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"ఛార్జింగ్లో లేనప్పుడు స్థితి పట్టీ చిహ్నం లోపల బ్యాటరీ స్థాయి శాతం చూపుతుంది"</string>
@@ -551,26 +571,27 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ఆఫ్లో ఉన్నాయి"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"పవర్ నోటిఫికేషన్ నియంత్రణలతో, మీరు యాప్ నోటిఫికేషన్ల కోసం ప్రాముఖ్యత స్థాయిని 0 నుండి 5 వరకు సెట్ చేయవచ్చు. \n\n"<b>"స్థాయి 5"</b>" \n- నోటిఫికేషన్ జాబితా పైభాగంలో చూపబడతాయి \n- పూర్తి స్క్రీన్ అంతరాయం అనుమతించబడుతుంది \n- ఎల్లప్పుడూ త్వరిత వీక్షణ అందించబడుతుంది \n\n"<b>"స్థాయి 4"</b>\n"- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎల్లప్పుడూ త్వరిత వీక్షణ అందించబడుతుంది \n\n"<b>"స్థాయి 3"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n\n"<b>"స్థాయి 2"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n- ఎప్పుడూ శబ్దం మరియు వైబ్రేషన్ చేయవు \n\n"<b>"స్థాయి 1"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n- ఎప్పుడూ శబ్దం లేదా వైబ్రేట్ చేయవు \n- లాక్ స్క్రీన్ మరియు స్థితి పట్టీ నుండి దాచబడతాయి \n- నోటిఫికేషన్ జాబితా దిగువ భాగంలో చూపబడతాయి \n\n"<b>"స్థాయి 0"</b>" \n- యాప్ నుండి అన్ని నోటిఫికేషన్లు బ్లాక్ చేయబడతాయి"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"నోటిఫికేషన్లు"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"మీరు ఇకపై ఈ నోటిఫికేషన్లను పొందరు"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> నోటిఫికేషన్ వర్గాలు"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"ఈ అనువర్తనానికి నోటిఫికేషన్ వర్గాలు లేవు"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"ఈ యాప్ నుండి వచ్చే నోటిఫికేషన్లను ఆఫ్ చేయలేరు"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="other">ఈ యాప్ నుంచి <xliff:g id="NUMBER_1">%s</xliff:g> నోటిఫికేషన్ వర్గాలలో 1</item>
- <item quantity="one">ఈ యాప్ నుంచి <xliff:g id="NUMBER_0">%s</xliff:g> నోటిఫికేషన్ వర్గంలో 1</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> మరియు మరో <xliff:g id="NUMBER_5">%3$d</xliff:g></item>
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> మరియు మరో <xliff:g id="NUMBER_2">%3$d</xliff:g></item>
- </plurals>
+ <!-- no translation found for notification_channel_disabled (344536703863700565) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing (8945102997083836858) -->
+ <skip />
+ <!-- no translation found for inline_stop_button (4172980096860941033) -->
+ <skip />
+ <!-- no translation found for inline_keep_button (6665940297019018232) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing_app (1723113469580031041) -->
+ <skip />
+ <!-- no translation found for notification_unblockable_desc (1037434112919403708) -->
+ <skip />
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> యొక్క నోటిఫికేషన్ నియంత్రణలు తెరవబడ్డాయి"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> యొక్క నోటిఫికేషన్ నియంత్రణలు మూసివేయబడ్డాయి"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"ఈ ఛానెల్ యొక్క నోటిఫికేషన్లను అనుమతించండి"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"అన్ని వర్గాలు"</string>
<string name="notification_more_settings" msgid="816306283396553571">"మరిన్ని సెట్టింగ్లు"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"అనుకూలీకరించండి: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <!-- no translation found for notification_app_settings (420348114670768449) -->
+ <skip />
<string name="notification_done" msgid="5279426047273930175">"పూర్తయింది"</string>
+ <!-- no translation found for inline_undo (558916737624706010) -->
+ <skip />
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"నోటిఫికేషన్ నియంత్రణలు"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"నోటిఫికేషన్ తాత్కాలిక ఆపివేత ఎంపికలు"</string>
@@ -725,6 +746,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"విస్తరింపజేయి"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"కనిష్టీకరించు"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"మూసివేయి"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"సెట్టింగ్లు"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"తీసివేయడానికి కిందికి లాగండి"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"మెను"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> చిత్రంలో చిత్రం రూపంలో ఉంది"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index d43efdd..5b31061 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s แตะเพื่อปิดเสียง"</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s ตัวควบคุมระดับเสียงแสดงอยู่ เลื่อนขึ้นเพื่อปิด"</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"ตัวควบคุมระดับเสียงซ่อนอยู่"</string>
+ <string name="output_title" msgid="5355078100792942802">"เอาต์พุตสื่อ"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"เอาต์พุตการโทรออก"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"ไม่พบอุปกรณ์"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"ไม่พบอุปกรณ์ ลองเปิด <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"บลูทูธ"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"บลูทูธและ Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"ตัวรับสัญญาณ UI ระบบ"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"แสดงเปอร์เซ็นต์ของแบตเตอรี่ในตัว"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"แสดงเปอร์เซ็นต์ของระดับแบตเตอรี่ภายในไอคอนแถบสถานะเมื่อไม่มีการชาร์จ"</string>
@@ -724,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"ขยาย"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"ย่อเล็กสุด"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"ปิด"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"ลากลงเพื่อปิด"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"เมนู"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> ใช้การแสดงภาพซ้อนภาพ"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 4aa9ca4..dadd57e 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. I-tap upang i-mute."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Ipinapakita ang mga kontrol ng volume ng %s. Mag-swipe pataas upang i-dismiss."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Nakatago ang mga kontrol ng volume"</string>
+ <string name="output_title" msgid="5355078100792942802">"Output ng media"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Output ng tawag sa telepono"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Walang nakitang device"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Walang nakitang device. Subukang i-on ang <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth at Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Tuner ng System UI"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Ipakita ang naka-embed na porsyento ng baterya"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Ipakita ang porsyento ng antas ng baterya na nasa icon ng status bar kapag nagcha-charge"</string>
@@ -724,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Palawakin"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"I-minimize"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Isara"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"I-drag pababa upang i-dismiss"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"Nasa picture-in-picture ang <xliff:g id="NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index d028fdd..d2d8ac6 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Sesi kapatmak için dokunun."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s ses denetimleri gösteriliyor. Kapatmak için hızlıca yukarı kaydırın."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Ses denetimleri gizlendi"</string>
+ <string name="output_title" msgid="5355078100792942802">"Medya çıkışı"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Telefon çağrısı çıkışı"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Cihaz bulunamadı"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Cihaz bulunamadı. <xliff:g id="SERVICE">%1$s</xliff:g> hizmetini açmayı deneyin"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Kablosuz"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth ve Kablosuz"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Sistem Arayüzü Ayarlayıcısı"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Yerleşik pil yüzdesini göster"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Şarj olmazken durum çubuğu simgesinin içinde pil düzeyi yüzdesini göster"</string>
@@ -724,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Genişlet"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Simge durumuna getir"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Kapat"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Kapatmak için aşağıya sürükleyin"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menü"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g>, pencere içinde pencere özelliğini kullanıyor"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 8fa1b72..0790847 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -35,7 +35,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Поточні"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Сповіщення"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Низький рівень заряду акумулятора"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Акумулятор майже розрядився. Увімкніть режим економії заряду акумулятора"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"Залишилося <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"На основі використання залишилося <xliff:g id="PERCENTAGE">%s</xliff:g> – близько <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Залишилося <xliff:g id="PERCENTAGE">%s</xliff:g> – близько <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Залишилося <xliff:g id="PERCENTAGE">%s</xliff:g>. Увімкнено режим економії заряду акумулятора."</string>
<string name="invalid_charger" msgid="4549105996740522523">"Заряджання USB не підтримується.\nВикористовуйте лише наданий у комплекті зарядний пристрій."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"Заряджання через USB не підтримується."</string>
@@ -69,14 +72,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Користувач поточного облікового запису не може вмикати налагодження USB. Щоб увімкнути цю функцію, увійдіть в обліковий запис основного користувача."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Масштабув. на весь екран"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Розтягнути на весь екран"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Знімок екрана"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Збереження знімка екрана..."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Збереження знімка екрана..."</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Зберігається знімок екрана."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Знімок екрана зроблено."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Торкніться, щоб переглянути знімок екрана."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Не вдалося зробити знімок екрана."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Не вдалося зберегти знімок екрана."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Не вдалося зберегти знімок екрана через обмежений обсяг пам’яті."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"Знімок екрана зберігається"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"Знімок екрана збережено"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Торкніться, щоб переглянути знімок екрана"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"Не вдалося зробити знімок екрана"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Не вдалося зберегти знімок екрана"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Не вдалося зберегти знімок екрана через обмежений обсяг пам’яті"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Додаток або адміністратор вашої організації не дозволяють робити знімки екрана"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Парам.передав.файлів через USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Підключити як медіапрогравач (MTP)"</string>
@@ -498,6 +502,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Двотональний багаточастотний аналоговий сигнал"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Спеціальні можливості"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Виклики"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"Дзвінок"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Вібросигнал"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"Без звуку"</string>
@@ -508,6 +513,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Торкніться, щоб вимкнути звук."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Показано регуляторів гучності: %s. Проведіть пальцем угору, щоб закрити."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Регулятори гучності сховано"</string>
+ <string name="output_title" msgid="5355078100792942802">"Вивід медіа-вмісту"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Вивід телефонного виклику"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Пристроїв не знайдено"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Пристроїв не знайдено. Увімкніть <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth і Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Показувати заряд акумулятора у відсотках"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Показувати заряд акумулятора у відсотках в рядку стану, коли пристрій не заряджається"</string>
@@ -555,30 +567,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Вимк."</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"За допомогою елементів керування сповіщеннями ви можете налаштувати пріоритет сповіщень додатка – від 0 до 5 рівня. \n\n"<b>"Рівень 5"</b>\n"- Показувати сповіщення вгорі списку \n- Виводити на весь екран \n- Завжди показувати короткі сповіщення \n\n"<b>"Рівень 4"</b>\n"- Не виводити на весь екран \n- Завжди показувати короткі сповіщення \n\n"<b>"Рівень 3"</b>\n"- Не виводити на весь екран \n- Не показувати короткі сповіщення \n\n"<b>"Рівень 2"</b>\n"- Не виводити на весь екран \n- Не показувати короткі сповіщення \n- Вимкнути звук і вібросигнал \n\n"<b>"Рівень 1"</b>\n"- Не виводити на весь екран \n- Не показувати короткі сповіщення \n- Вимкнути звук і вібросигнал \n- Не показувати на заблокованому екрані та в рядку стану \n- Показувати сповіщення внизу списку \n\n"<b>"Рівень 0"</b>\n"- Блокувати всі сповіщення з додатка"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Сповіщення"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"Ви більше не отримуватимете ці сповіщення"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"Категорії сповіщень (<xliff:g id="NUMBER">%d</xliff:g>)"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"У цьому додатку немає категорій сповіщень"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Сповіщення з цього додатка не можна вимкнути"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="one">1 з <xliff:g id="NUMBER_1">%s</xliff:g> категорії сповіщень із цього додатка</item>
- <item quantity="few">1 з <xliff:g id="NUMBER_1">%s</xliff:g> категорій сповіщень із цього додатка</item>
- <item quantity="many">1 з <xliff:g id="NUMBER_1">%s</xliff:g> категорій сповіщень із цього додатка</item>
- <item quantity="other">1 з <xliff:g id="NUMBER_1">%s</xliff:g> категорії сповіщень із цього додатка</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> і ще <xliff:g id="NUMBER_5">%3$d</xliff:g></item>
- <item quantity="few"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> і ще <xliff:g id="NUMBER_5">%3$d</xliff:g></item>
- <item quantity="many"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> і ще <xliff:g id="NUMBER_5">%3$d</xliff:g></item>
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> і ще <xliff:g id="NUMBER_5">%3$d</xliff:g></item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"Ви більше не бачитимете цих сповіщень"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"Чи показувати ці сповіщення надалі?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"Не показувати сповіщення"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Показувати надалі"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"Чи показувати сповіщення з цього додатка надалі?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"Ці сповіщення не можна вимкнути"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Елементи керування сповіщеннями для додатка <xliff:g id="APP_NAME">%1$s</xliff:g> відкрито"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Елементи керування сповіщеннями для додатка <xliff:g id="APP_NAME">%1$s</xliff:g> закрито"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Дозволити сповіщення з цього каналу"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"Усі категорії"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Більше налаштувань"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Налаштувати: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"Налаштувати"</string>
<string name="notification_done" msgid="5279426047273930175">"Готово"</string>
+ <string name="inline_undo" msgid="558916737624706010">"Відмінити"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"елементи керування сповіщеннями"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"параметри відкладення сповіщень"</string>
@@ -737,6 +738,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Розгорнути"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Згорнути"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Закрити"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Налаштування"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Перетягніть униз, щоб закрити"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Меню"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"У додатку <xliff:g id="NAME">%s</xliff:g> є функція \"Картинка в картинці\""</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 73fd5886..e202363 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -33,7 +33,13 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"جاری"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"اطلاعات"</string>
<string name="battery_low_title" msgid="6456385927409742437">"بیٹری کم ہے"</string>
+ <!-- no translation found for battery_low_title_hybrid (6268991275887381595) -->
+ <skip />
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> باقی ہے"</string>
+ <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) -->
+ <skip />
+ <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) -->
+ <skip />
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> باقی ہے۔ بیٹری سیور آن ہے۔"</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB چارجنگ تعاون یافتہ نہیں ہے.\nصرف فراہم کردہ چارجر کا ہی استعمال کریں۔"</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"USB چارجنگ تعاون یافتہ نہیں ہے۔"</string>
@@ -67,14 +73,22 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"اس آلہ پر فی الحال سائن ان کردہ صارف USB ڈیبگنگ آن نہیں کر سکتا۔ اس خصوصیت کا استعمال کرنے کیلئے، ابتدائی صارف پر سوئچ کریں۔"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"پوری سکرین پر زوم کریں"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"پوری سکرین پر پھیلائیں"</string>
+ <!-- no translation found for global_action_screenshot (8329831278085426283) -->
+ <skip />
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"اسکرین شاٹ محفوظ ہو رہا ہے…"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"اسکرین شاٹ محفوظ ہو رہا ہے…"</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"اسکرین شاٹ محفوظ کیا جا رہا ہے۔"</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"اسکرین شاٹ کیپچر کیا گیا۔"</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"اپنا اسکرین شاٹ دیکھنے کیلئے تھپتھپائیں۔"</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"اسکرین شاٹ کیپچر نہیں کر سکے۔"</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"اسکرین شاٹ محفوظ کرتے وقت مسئلہ پیش آ گیا۔"</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"محدود اسٹوریج جگہ کی وجہ سے اسکرین شاٹس نہیں لئے جا سکتے۔"</string>
+ <!-- no translation found for screenshot_saving_text (2545047868936087248) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_title (5637073968117370753) -->
+ <skip />
+ <!-- no translation found for screenshot_saved_text (7574667448002050363) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_title (9096484883063264803) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) -->
+ <skip />
+ <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) -->
+ <skip />
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"ایپ یا آپ کی تنظیم کی جانب سے اسکرین شاٹس لینے کی اجازت نہیں ہے"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB فائل منتقل کرنیکے اختیارات"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"ایک میڈیا پلیئر (MTP) کے بطور ماؤنٹ کریں"</string>
@@ -492,8 +506,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"بلوٹوتھ"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"دوہری ملٹی ٹون فریکوئنسی"</string>
<string name="stream_accessibility" msgid="301136219144385106">"ایکسیسبیلٹی"</string>
- <!-- no translation found for ring_toggle_title (3281244519428819576) -->
- <skip />
+ <string name="ring_toggle_title" msgid="3281244519428819576">"کالز"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"رِنگ کریں"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"وائبریٹ"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"خاموش کریں"</string>
@@ -504,6 +517,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s۔ خاموش کرنے کیلئے تھپتھپائیں۔"</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s والیوم کے کنٹرولز دکھائے جا رہے ہیں۔ برخاست کرنے کیلئے سوائپ کریں۔"</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"والیوم کے کنٹرولز مخفی ہیں"</string>
+ <string name="output_title" msgid="5355078100792942802">"میڈیا آؤٹ پٹ"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"فون کال کا آؤٹ پٹ"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"کوئی آلہ نہیں ملا"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"کوئی آلہ نہیں ملا۔ <xliff:g id="SERVICE">%1$s</xliff:g> آن کر کے آزمائیں"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"بلوٹوتھ"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"بلوٹوتھ اور Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"سسٹم UI ٹیونر"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"سرایت کردہ بیٹری کی فیصد دکھائیں"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"جب چارج نہ ہو رہا ہو تو بیٹری کی سطح کی فیصد اسٹیٹس بار آئیکن کے اندر دکھائیں"</string>
@@ -551,26 +571,27 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"آف"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"پاور اطلاع کنٹرولز کے ساتھ آپ کسی ایپ کی اطلاعات کیلئے 0 سے 5 تک اہمیت کی سطح سیٹ کر سکتے ہیں۔ \n\n"<b>"سطح 5"</b>\n"- اطلاعات کی فہرست کے اوپر دکھائیں \n- پوری اسکرین کی مداخلت کی اجازت دیں \n- ہمیشہ جھانکنا\n\n"<b>"سطح 4"</b>\n"- پوری اسکرین کی مداخلت کو روکیں \n- ہمیشہ جھانکنا\n\n"<b>"سطح 3"</b>\n"- پوری اسکرین کی مداخلت کو روکیں \n- کبھی نہ جھانکنا \n\n"<b>"سطح 2"</b>\n"- پوری اسکرین کی مداخلت کو روکیں \n- کبھی نہ جھانکنا \n- کبھی آواز اور ارتعاش پیدا نہ کرنا \n\n"<b>" سطح 1"</b>\n"- پوری اسکرین کی مداخلت کو روکنا \n- کبھی نہ جھانکنا \n- کبھی بھی آواز یا ارتعاش پیدا نہ کرنا\n- مقفل اسکرین اور اسٹیٹس بار سے چھپانا \n - اطلاع کی فہرست کی نیچے دکھانا \n\n"<b>"سطح 0"</b>\n"- ایپ سے تمام اطلاعات مسدود کریں"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"اطلاعات"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"آپ کو یہ اطلاعات مزید نہیں ملیں گی"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"اطلاع کے <xliff:g id="NUMBER">%d</xliff:g> زمرے"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"اس ایپ میں اطلاعاتی زمرے نہیں ہیں"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"اس ایپ کی اطلاعات کو آف نہیں کیا جا سکتا"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="other">اس ایپ کے <xliff:g id="NUMBER_1">%s</xliff:g> اطلاعاتی زمروں میں سے 1</item>
- <item quantity="one">اس ایپ کے <xliff:g id="NUMBER_0">%s</xliff:g> اطلاعاتی زمرے میں سے 1</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>، <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>، <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> اور <xliff:g id="NUMBER_5">%3$d</xliff:g> دیگر</item>
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>، <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> اور <xliff:g id="NUMBER_2">%3$d</xliff:g> دیگر</item>
- </plurals>
+ <!-- no translation found for notification_channel_disabled (344536703863700565) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing (8945102997083836858) -->
+ <skip />
+ <!-- no translation found for inline_stop_button (4172980096860941033) -->
+ <skip />
+ <!-- no translation found for inline_keep_button (6665940297019018232) -->
+ <skip />
+ <!-- no translation found for inline_keep_showing_app (1723113469580031041) -->
+ <skip />
+ <!-- no translation found for notification_unblockable_desc (1037434112919403708) -->
+ <skip />
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> کیلئے اطلاعی کنٹرولز کھلے ہیں"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> کیلئے اطلاعی کنٹرولز بند کر دئے گئے ہیں"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"اس چینل سے اطلاعات کی اجازت دیں"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"سبھی زمرے"</string>
<string name="notification_more_settings" msgid="816306283396553571">"مزید ترتیبات"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"حسب ضرورت بنائیں: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <!-- no translation found for notification_app_settings (420348114670768449) -->
+ <skip />
<string name="notification_done" msgid="5279426047273930175">"ہوگیا"</string>
+ <!-- no translation found for inline_undo (558916737624706010) -->
+ <skip />
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"اطلاع کے کنٹرولز"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"اطلاع اسنوز کرنے کے اختیارات"</string>
@@ -725,6 +746,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"پھیلائیں"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"چھوٹی کریں"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"بند کریں"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"ترتیبات"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"برخاست کرنے کیلئے نیچے گھسیٹیں"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"مینو"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> تصویر میں تصویر میں ہے"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 55eb4fb..a8b5e54 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -33,7 +33,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Hali bajarilmagan"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Eslatmalar"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Batareya quvvati kam qoldi"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Batareya quvvati kam qoldi. Quvvat tejash rejimini yoqing."</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> qoldi"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> (joriy holatda taxminan <xliff:g id="TIME">%s</xliff:g> qoldi)"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> (taxminan <xliff:g id="TIME">%s</xliff:g> qoldi)"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> qoldi. Quvvat tejash rejimi yoniq."</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB orqali zaryadlab bo‘lmaydi.\nFaqat taklif qilingan zaryadlagichdan foydalaning."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"USB orqali quvvat oldirish qo‘llab-quvvatlanmaydi."</string>
@@ -67,14 +70,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Ayni paytda ushbu qurilmaga o‘z hisobi bilan kirgan foydalanuvchi USB orqali nosozliklarni tuzatish funksiyasini yoqa olmaydi. Bu funksiyadan foydalanish uchun asosiy foydalanuvchi profiliga o‘ting."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Ekranga moslashtirish"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Ekran hajmida cho‘zish"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Skrinshot"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Skrinshot saqlanmoqda…"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Skrinshot saqlanmoqda…"</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Skrinshot saqlanmoqda."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Skrinshot saqlandi."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Skrinshotni ko‘rish uchun bosing."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Skrinshot saqlanmadi."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Skrinshotni saqlashda muammo yuz berdi."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Xotirada joy kamligi uchun skrinshotni saqlab bo‘lmadi."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"Skrinshot saqlanmoqda"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"Skrinshot saqlandi"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Skrinshotni ko‘rish uchun bosing"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"Skrinshot saqlanmadi"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Skrinshotni saqlashda muammo yuz berdi"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Xotirada joy kamligi uchun skrinshot saqlanmadi"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Ilova yoki tashkilotingiz skrinshot olishni taqiqlagan"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB fayl ko‘chirish moslamalari"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Media pleyer sifatida ulash (MTP)"</string>
@@ -505,6 +509,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Ovozsiz qilish uchun ustiga bosing."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Ovoz balandligini boshqarish tugmalari ko‘rsatilgan: %s. Yopish uchun tepaga suring."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Ovoz balandligini boshqarish tugmalari yashirilgan"</string>
+ <string name="output_title" msgid="5355078100792942802">"Media chiqishi"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Telefon chaqiruvlari"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Hech qanday qurilma topilmadi"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Hech qanday qurilma topilmadi. <xliff:g id="SERVICE">%1$s</xliff:g> aloqasini yoqing."</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth va Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"SystemUI Tuner"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Batareya foizini chiqarish"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Quvvat olmayotgan vaqtda batareya foizi holat qatorida chiqib turadi"</string>
@@ -552,26 +563,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"O‘chiq"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"Bildirishnomalar uchun kengaytirilgan boshqaruv yordamida ilova bildirishnomalarining muhimlik darajasini (0-5) sozlash mumkin. \n\n"<b>"5-daraja"</b>" \n- Bildirishnomani ro‘yxatning boshida ko‘rsatish \n- To‘liq ekranli bildirishnomalarni ko‘rsatish \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatish \n\n"<b>"4-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatish \n\n"<b>"3-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatmaslik \n\n"<b>"2-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatmaslik \n- Ovoz va tebranishdan foydalanmaslik \n\n"<b>"1-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatmaslik \n- Ovoz va tebranishdan foydalanmaslik \n- Ekran qulfi va holat qatorida ko‘rsatmaslik \n- Bildirishnomani ro‘yxatning oxirida ko‘rsatish \n\n"<b>"0-daraja"</b>" \n- Ilovadan keladigan barcha bildirishnomalarni bloklash"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Bildirishnomalar"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"Ushbu bildirishnomalar endi ko‘rsatilmaydi"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> ta bildirishnoma turkumi"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"Bu ilovada bildirishnomalar turkumi yo‘q"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Bu ilova bildirishnomalarini o‘chirib bo‘lmaydi"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="other">Bu ilovadagi <xliff:g id="NUMBER_1">%s</xliff:g> ta bildirishnomalar turkumidan 1 tasi</item>
- <item quantity="one">Bu ilovadagi <xliff:g id="NUMBER_0">%s</xliff:g> ta bildirishnomalar turkumidan 1 tasi</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> va yana <xliff:g id="NUMBER_5">%3$d</xliff:g> ta</item>
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> va yana <xliff:g id="NUMBER_2">%3$d</xliff:g> ta</item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"Ushbu bildirishnomalar endi ko‘rsatilmaydi"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"Mazkur bildirishnomalar ko‘rsatishda davom etilsinmi?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"Bildirishnomalarni to‘xtatish"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Ko‘rsatilsin"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"Bu ilovadan keladigan bildirishnomalar ko‘rsatishda davom etilsinmi?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"Ushbu bildirishnomalarni o‘chirib bo‘lmaydi"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun bildirishnoma sozlamalari ochildi"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun bildirishnoma sozlamalari yopildi"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Bu kanaldan keladigan bildirishnomalarga ruxsat berish"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"Barcha turkumlar"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Boshqa sozlamalar"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"<xliff:g id="SUB_CATEGORY">%1$s</xliff:g>: sozlash"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"Moslash"</string>
<string name="notification_done" msgid="5279426047273930175">"Tayyor"</string>
+ <string name="inline_undo" msgid="558916737624706010">"Qaytarish"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"bildirishnoma sozlamalari"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"bildirishnomalarni kechiktirish parametrlari"</string>
@@ -726,6 +730,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Yoyish"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Yig‘ish"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Yopish"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Sozlamalar"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Yopish uchun pastga torting"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menyu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> tasvir ustida tasvir rejimida"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 551decf..75b18ca 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -505,6 +505,13 @@
<!-- no translation found for volume_dialog_accessibility_shown_message (1834631467074259998) -->
<skip />
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Các điều khiển âm lượng bị ẩn"</string>
+ <string name="output_title" msgid="5355078100792942802">"Đầu ra phương tiện"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Đầu ra cuộc gọi điệnt thoại"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Không tìm thấy thiết bị nào"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Không tìm thấy thiết bị nào. Hãy thử bật <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"Bluetooth và Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Bộ điều hướng giao diện người dùng hệ thống"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Hiển thị tỷ lệ phần trăm pin được nhúng"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Hiển thị tỷ lệ phần trăm mức pin bên trong biểu tượng thanh trạng thái khi không sạc"</string>
@@ -726,6 +733,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Mở rộng"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Thu nhỏ"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Đóng"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Kéo xuống để loại bỏ"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> đang ở chế độ ảnh trong ảnh"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 2b64d86..16161ee 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -492,8 +492,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"蓝牙"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"双音多频"</string>
<string name="stream_accessibility" msgid="301136219144385106">"无障碍"</string>
- <!-- no translation found for ring_toggle_title (3281244519428819576) -->
- <skip />
+ <string name="ring_toggle_title" msgid="3281244519428819576">"通话"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"响铃"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"振动"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"静音"</string>
@@ -504,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s。点按即可设为静音。"</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"已显示%s音量控件。向上滑动即可关闭。"</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"已隐藏音量控件"</string>
+ <string name="output_title" msgid="5355078100792942802">"媒体输出"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"通话输出"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"未找到任何设备"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"未找到任何设备。请尝试开启<xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"蓝牙"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"WLAN"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"蓝牙和 WLAN"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"系统界面调节工具"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"嵌入式显示电池电量百分比 显示嵌入的电池电量百分比"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"未充电时在状态栏图标内显示电池电量百分比"</string>
@@ -725,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"展开"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"最小化"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"关闭"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"向下拖动即可关闭"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"菜单"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g>目前位于“画中画”中"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 382425d..e0ab7e4 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -505,6 +505,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s。輕按即可設為靜音。"</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"已顯示 %s 音量控制項。向上快速滑動即可關閉。"</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"已隱藏音量控制"</string>
+ <string name="output_title" msgid="5355078100792942802">"媒體輸出"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"通話輸出"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"找不到裝置"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"找不到裝置,請嘗試開啟<xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"藍牙"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"藍牙和 Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"系統使用者介面調諧器"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"顯示嵌入的電池百分比"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"非充電時,在狀態列圖示顯示電量百分比"</string>
@@ -726,6 +733,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"展開"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"最小化"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"關閉"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"向下拖曳即可關閉"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"選單"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"「<xliff:g id="NAME">%s</xliff:g>」目前在畫中畫模式"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index ef9ea43..2d43da8 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -503,6 +503,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s。輕觸即可設為靜音。"</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"已顯示 %s 個音量控制項。向上滑動即可關閉。"</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"已隱藏音量控制項"</string>
+ <string name="output_title" msgid="5355078100792942802">"媒體輸出"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"通話輸出"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"找不到裝置"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"找不到裝置,請嘗試開啟「<xliff:g id="SERVICE">%1$s</xliff:g>」"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"藍牙"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"藍牙和 Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"系統使用者介面調整精靈"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"顯示嵌入式電池百分比"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"未充電時在狀態列圖示中顯示電量百分比"</string>
@@ -724,6 +731,8 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"展開"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"最小化"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"關閉"</string>
+ <!-- no translation found for pip_phone_settings (8080777499521528521) -->
+ <skip />
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"向下拖曳即可關閉"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"選單"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"「<xliff:g id="NAME">%s</xliff:g>」目前在子母畫面中"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index b3b26dc..a0eb56f 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -33,7 +33,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Okuqhubekayo"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Izaziso"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Ibhethri liphansi"</string>
+ <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Ibhethri liphansi. Vula isilondolozi sebhethri"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> okusele"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> okusele, cishe u-<xliff:g id="TIME">%s</xliff:g> osele ngokusukela ekusebenziseni kwakho"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> okusele, cishe u-<xliff:g id="TIME">%s</xliff:g> osele"</string>
<string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> esele. Isilondolozi sebhethri sivuliwe."</string>
<string name="invalid_charger" msgid="4549105996740522523">"Ukushaja i-USB akusekelwe.\nSebenzisa kuphela ishaja enikeziwe."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"Ukushaja kwe-USB akusekelwe."</string>
@@ -67,14 +70,15 @@
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Umsebenzisi manje ongene ngemvume kule divayisi entsha akakwazi ukuvula ukulungisa amaphutha ku-USB. Ukuze usebenzise lesi sici, shintshela kumsebenzisi oyinhloko."</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Sondeza ukugcwalisa isikrini"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Nweba ukugcwalisa isikrini"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Isithombe-skrini"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"Ilondoloz umfanekiso weskrini..."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"Ilondoloz umfanekiso weskrini..."</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Umfanekiso weskrini uyalondolozwa."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Umfanekiso weskrini uqoshiwe"</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Thepha ukuze ubuke isithombe-skrini sakho."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Yehlulekile ukulondoloza umfanekiso weskrini."</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Inkinga ivelile ngenkathi ilondoloza isithombe sikrini."</string>
- <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Ayikwazi ukulondoloza isithombe-skrini ngenxa yesikhala sesitoreji esikhawulelwe."</string>
+ <string name="screenshot_saving_text" msgid="2545047868936087248">"Umfanekiso weskrini uyalondolozwa"</string>
+ <string name="screenshot_saved_title" msgid="5637073968117370753">"Isithombe-skrini silondoloziwe"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Thepha ukuze ubuke isithombe-skrini sakho"</string>
+ <string name="screenshot_failed_title" msgid="9096484883063264803">"Yehlulekile ukuthwebula umfanekiso weskrini"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Inkinga ivelile ngenkathi ilondoloza isithombe sikrini"</string>
+ <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Ayikwazi ukulondoloza isithombe-skrini ngenxa yesikhala sesitoreji esikhawulelwe"</string>
<string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Ukuthatha izithombe-skrini akuvunyelwe uhlelo lokusebenza noma inhlangano yakho"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Okukhethwa kokudluliswa kwefayela ye-USB"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Lengisa njengesidlali semediya (MTP)"</string>
@@ -492,6 +496,7 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"I-Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Ifrikhwensi yethoni engakuningi"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Ukufinyeleleka"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Amakholi"</string>
<string name="volume_ringer_status_normal" msgid="4273142424125855384">"Khalisa"</string>
<string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Dlidlizela"</string>
<string name="volume_ringer_status_silent" msgid="6896394161022916369">"Thulisa"</string>
@@ -502,6 +507,13 @@
<string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Thepha ukuze uthulise."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"%s izilawuli zevolumu ziyaboniswa. Swayiphela phezulu ukuze ulahle."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Izilawuli zevolumi zifihliwe"</string>
+ <string name="output_title" msgid="5355078100792942802">"Okukhiphayo kwemidiya"</string>
+ <string name="output_calls_title" msgid="8717692905017206161">"Okukhiphayo kwekholi yefoni"</string>
+ <string name="output_none_found" msgid="5544982839808921091">"Awekho amadivayisi atholiwe"</string>
+ <string name="output_none_found_service_off" msgid="8631969668659757069">"Awekho amadivayisi atholiwe. Zama ukuvula i-<xliff:g id="SERVICE">%1$s</xliff:g>"</string>
+ <string name="output_service_bt" msgid="6224213415445509542">"I-Bluetooth"</string>
+ <string name="output_service_wifi" msgid="3749735218931825054">"I-Wi-Fi"</string>
+ <string name="output_service_bt_wifi" msgid="4486837869988770896">"I-Bluetooth ne-Wi-Fi"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Isishuni se-UI yesistimu"</string>
<string name="show_battery_percentage" msgid="5444136600512968798">"Bonisa amaphesenti ebhethri elinamathiselwe"</string>
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Bonisa amaphesenti eleveli yebhethri ngaphakathi kwesithonjana sebha yesimo uma kungashajwa"</string>
@@ -549,26 +561,19 @@
<string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Valiwe"</string>
<string name="power_notification_controls_description" msgid="4372459941671353358">"Ngezilawuli zesaziso zamandla, ungasetha ileveli ebalulekile kusuka ku-0 kuya ku-5 kusuka kuzaziso zohlelo lokusebenza. \n\n"<b>"Ileveli 5"</b>" \n- Ibonisa phezulu kuhlu lwesaziso \n- Vumela ukuphazamiseka kwesikrini esigcwele \n- Ukuhlola njalo \n\n"<b>"Ileveli 4"</b>" \n- Gwema ukuphazamiseka kwesikrini esigcwele \n- Ukuhlola njalo \n\n"<b>"Ileveli 3"</b>" \n- Gwema ukuphazamiseka kwesikrini esigcwele \n- Ukungahloli \n\n"<b>"Ileveli 2"</b>" \n- Gwema ukuphazamiseka kwesikrini esigcwele \n- Ukungahloli \n- Ungenzi umsindo nokudlidliza \n\n"<b>"Ileveli 1"</b>" \n- Gwema ukuphazamiseka kwesikrini esigcwele \n- Ukungahloli \n- Ungenzi umsindo noma ukudlidliza \n- Fihla kusuka kusikrini sokukhiya nebha yesimo \n- Bonisa phansi kohlu lwesaziso \n\n"<b>"Ileveli 0"</b>" \n- Vimbela zonke izaziso kusuka kuhlelo lokusebenza"</string>
<string name="notification_header_default_channel" msgid="7506845022070889909">"Izaziso"</string>
- <string name="notification_channel_disabled" msgid="2139193533791840539">"Ngeke usathola izaziso"</string>
- <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> izigaba zesaziso"</string>
- <string name="notification_default_channel_desc" msgid="2506053815870808359">"Lolu hlelo lokusebenza alunazo izigaba zesaziso"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Izaziso kusuka kulolu hlelo lokusebenza azikwazi ukuvalwa"</string>
- <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
- <item quantity="one">1 isigaba kwezingu-<xliff:g id="NUMBER_1">%s</xliff:g> sezaziso kusukela kulolu hlelo lokusebenza</item>
- <item quantity="other">1 isigaba kwezingu-<xliff:g id="NUMBER_1">%s</xliff:g> sezaziso kusukela kulolu hlelo lokusebenza</item>
- </plurals>
- <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
- <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
- <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, nabanye abangu-<xliff:g id="NUMBER_5">%3$d</xliff:g></item>
- <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, nabanye abangu-<xliff:g id="NUMBER_5">%3$d</xliff:g></item>
- </plurals>
+ <string name="notification_channel_disabled" msgid="344536703863700565">"Ngeke usabona lezi zaziso"</string>
+ <string name="inline_keep_showing" msgid="8945102997083836858">"Qhubeka nokubonisa lezi zaziso?"</string>
+ <string name="inline_stop_button" msgid="4172980096860941033">"Misa izaziso"</string>
+ <string name="inline_keep_button" msgid="6665940297019018232">"Qhubeka nokubonisa"</string>
+ <string name="inline_keep_showing_app" msgid="1723113469580031041">"Qhubeka nokubonisa izaziso kusuka kulolu hlelo lokusebenza?"</string>
+ <string name="notification_unblockable_desc" msgid="1037434112919403708">"Lezi zaziso azikwazi ukuvalwa"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Izilawuli zesaziso ze-<xliff:g id="APP_NAME">%1$s</xliff:g> zivuliwe"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Izilawuli zesaziso ze-<xliff:g id="APP_NAME">%1$s</xliff:g> zivaliwe"</string>
<string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Zonke izaziso kusuka kulesi siteshi"</string>
- <string name="notification_all_categories" msgid="5407190218055113282">"Zonke izigaba"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Izilungiselelo eziningi"</string>
- <string name="notification_app_settings" msgid="3743278649182392015">"Enza ngendlela oyifisayo: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string>
+ <string name="notification_app_settings" msgid="420348114670768449">"Enza ngendlela oyifisayo"</string>
<string name="notification_done" msgid="5279426047273930175">"Kwenziwe"</string>
+ <string name="inline_undo" msgid="558916737624706010">"Susa"</string>
<string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="2204480013726775108">"izilawuli zesaziso"</string>
<string name="notification_menu_snooze_description" msgid="3653669438131034525">"izinketho zokusnuza zesaziso"</string>
@@ -723,6 +728,7 @@
<string name="pip_phone_expand" msgid="5889780005575693909">"Nweba"</string>
<string name="pip_phone_minimize" msgid="1079119422589131792">"Nciphisa"</string>
<string name="pip_phone_close" msgid="8416647892889710330">"Vala"</string>
+ <string name="pip_phone_settings" msgid="8080777499521528521">"Izilungiselelo"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Hudulela phansi ukuze ucashise"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Imenyu"</string>
<string name="pip_notification_title" msgid="3204024940158161322">"U-<xliff:g id="NAME">%s</xliff:g> ungaphakathi kwesithombe esiphakathi kwesithombe"</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index f244d88..0f4c3b8 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -143,6 +143,9 @@
<color name="remote_input_accent">#eeeeee</color>
+ <color name="quick_step_track_background_dark">#61000000</color>
+ <color name="quick_step_track_background_light">#4DFFFFFF</color>
+
<!-- Keyboard shortcuts colors -->
<color name="ksh_application_group_color">#fff44336</color>
<color name="ksh_keyword_color">#d9000000</color>
@@ -153,4 +156,6 @@
<color name="zen_introduction">#ffffffff</color>
+ <color name="smart_reply_button_text">#ff4285f4</color><!-- blue 500 -->
+ <color name="smart_reply_button_background">#fff7f7f7</color>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 60e9ebf..7a670fd 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -78,10 +78,10 @@
<dimen name="status_bar_connected_device_bt_indicator_size">17dp</dimen>
<!-- Height of a small notification in the status bar-->
- <dimen name="notification_min_height">100dp</dimen>
+ <dimen name="notification_min_height">106dp</dimen>
<!-- Increased height of a small notification in the status bar -->
- <dimen name="notification_min_height_increased">140dp</dimen>
+ <dimen name="notification_min_height_increased">146dp</dimen>
<!-- Height of a small notification in the status bar which was used before android N -->
<dimen name="notification_min_height_legacy">64dp</dimen>
@@ -90,7 +90,7 @@
<dimen name="notification_min_height_before_p">92dp</dimen>
<!-- Height of a large notification in the status bar -->
- <dimen name="notification_max_height">292dp</dimen>
+ <dimen name="notification_max_height">294dp</dimen>
<!-- Height of an ambient notification on ambient display -->
<dimen name="notification_ambient_height">400dp</dimen>
@@ -102,16 +102,13 @@
<dimen name="notification_max_heads_up_height_before_p">148dp</dimen>
<!-- Height of a heads up notification in the status bar -->
- <dimen name="notification_max_heads_up_height">156dp</dimen>
+ <dimen name="notification_max_heads_up_height">162dp</dimen>
<!-- Height of a heads up notification in the status bar -->
<dimen name="notification_max_heads_up_height_increased">188dp</dimen>
<!-- Side padding on the lockscreen on the side of notifications -->
- <dimen name="notification_lockscreen_side_paddings">8dp</dimen>
-
- <!-- Additional side padding for custom content if the app doesn't target P yet -->
- <dimen name="notification_content_custom_view_side_padding">@dimen/notification_lockscreen_side_paddings</dimen>
+ <dimen name="notification_side_paddings">4dp</dimen>
<!-- Height of a messaging notifications with actions at least. Not that this is an upper bound
and the notification won't use this much, but is measured with wrap_content -->
@@ -127,7 +124,7 @@
<dimen name="notification_min_interaction_height">40dp</dimen>
<!-- the padding of the shelf icon container -->
- <dimen name="shelf_icon_container_padding">21dp</dimen>
+ <dimen name="shelf_icon_container_padding">13dp</dimen>
<!-- The padding of a notification icon on top to the start of the notification. Used for custom
views where the distance can't be measured -->
@@ -148,6 +145,12 @@
<!-- The space around a notification menu item -->
<dimen name="notification_menu_icon_padding">20dp</dimen>
+ <!-- The veritical space around the buttons in the inline settings -->
+ <dimen name="notification_guts_button_spacing">20dp</dimen>
+
+ <!-- The height of the header in inline settings -->
+ <dimen name="notification_guts_header_height">24dp</dimen>
+
<!-- The minimum height for the snackbar shown after the snooze option has been chosen. -->
<dimen name="snooze_snackbar_min_height">56dp</dimen>
@@ -232,7 +235,7 @@
<dimen name="qs_footer_height">48dp</dimen>
<!-- The padding between the notifications and the quick settings container -->
- <dimen name="qs_notification_keyguard_padding">8dp</dimen>
+ <dimen name="qs_notification_padding">@dimen/notification_side_paddings</dimen>
<!-- Height of the status bar header bar when expanded -->
<dimen name="status_bar_header_height_expanded">124dp</dimen>
@@ -261,7 +264,7 @@
<!-- The width of the panel that holds the quick settings. -->
<dimen name="qs_panel_width">@dimen/notification_panel_width</dimen>
- <dimen name="volume_dialog_panel_width">315dp</dimen>
+ <dimen name="volume_dialog_panel_width">120dp</dimen>
<!-- Gravity for the notification panel -->
<integer name="notification_panel_layout_gravity">0x31</integer><!-- center_horizontal|top -->
@@ -385,9 +388,6 @@
group. -->
<dimen name="notification_children_container_divider_height">@dimen/notification_divider_height</dimen>
- <!-- The height of the header for a container containing child notifications. -->
- <dimen name="notification_children_container_header_height">53dp</dimen>
-
<!-- The top margin for the notification children container in its non-expanded form. -->
<dimen name="notification_children_container_margin_top">@*android:dimen/notification_content_margin_top</dimen>
@@ -579,6 +579,7 @@
<dimen name="keyguard_affordance_icon_width">24dp</dimen>
<dimen name="keyguard_indication_margin_bottom">65dp</dimen>
+ <dimen name="keyguard_indication_margin_bottom_ambient">30dp</dimen>
<!-- The text size for battery level -->
<dimen name="battery_level_text_size">12sp</dimen>
@@ -870,6 +871,8 @@
<dimen name="rounded_corner_radius">0dp</dimen>
<dimen name="rounded_corner_content_padding">0dp</dimen>
<dimen name="nav_content_padding">0dp</dimen>
+ <dimen name="nav_quick_scrub_track_edge_padding">32dp</dimen>
+ <dimen name="nav_quick_scrub_track_thickness">2dp</dimen>
<!-- Intended corner radius when drawing the mobile signal -->
<dimen name="stat_sys_mobile_signal_corner_radius">0.75dp</dimen>
@@ -879,4 +882,9 @@
<!-- Home button padding for sizing -->
<dimen name="home_padding">15dp</dimen>
+ <!-- Smart reply button -->
+ <dimen name="smart_reply_button_corner_radius">24dip</dimen>
+ <dimen name="smart_reply_button_spacing">8dp</dimen>
+ <dimen name="smart_reply_button_padding_vertical">4dp</dimen>
+ <dimen name="smart_reply_button_font_size">14sp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index dd31365..fed97c5 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -86,10 +86,6 @@
<item type="id" name="top_roundess_animator_start_tag"/>
<item type="id" name="top_roundess_animator_end_tag"/>
- <item type="id" name="side_padding_animator_tag"/>
- <item type="id" name="side_padding_animator_start_tag"/>
- <item type="id" name="side_padding_animator_end_tag"/>
-
<!-- Accessibility actions for the notification menu -->
<item type="id" name="action_snooze_undo"/>
<item type="id" name="action_snooze_shorter"/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 98537a1..dde4dcf 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -61,11 +61,26 @@
<!-- When the battery is low, this is displayed to the user in a dialog. The title of the low battery alert. [CHAR LIMIT=NONE]-->
<string name="battery_low_title">Battery is low</string>
+ <!-- When the battery is low and hybrid notifications are enabled, this is displayed to the user in a dialog.
+ The title of the low battery alert. [CHAR LIMIT=NONE]-->
+ <string name="battery_low_title_hybrid">Battery is low. Turn on Battery Saver</string>
+
<!-- A message that appears when the battery level is getting low in a dialog. This is
- appened to the subtitle of the low battery alert. "percentage" is the percentage of battery
+ appended to the subtitle of the low battery alert. "percentage" is the percentage of battery
remaining [CHAR LIMIT=none]-->
<string name="battery_low_percent_format"><xliff:g id="percentage">%s</xliff:g> remaining</string>
+ <!-- A message that appears when the battery remaining estimate is low in a dialog. This is
+ appended to the subtitle of the low battery alert. "percentage" is the percentage of battery
+ remaining. "time" is the amount of time remaining before the phone runs out of battery [CHAR LIMIT=none]-->
+ <string name="battery_low_percent_format_hybrid"><xliff:g id="percentage">%s</xliff:g> remaining, about <xliff:g id="time">%s</xliff:g> left based on your usage</string>
+
+ <!-- A message that appears when the battery remaining estimate is low in a dialog and insufficient
+ data was present to say it is customized to the user. This is appended to the subtitle of the
+ low battery alert. "percentage" is the percentage of battery remaining. "time" is the amount
+ of time remaining before the phone runs out of battery [CHAR LIMIT=none]-->
+ <string name="battery_low_percent_format_hybrid_short"><xliff:g id="percentage">%s</xliff:g> remaining, about <xliff:g id="time">%s</xliff:g> left</string>
+
<!-- Same as battery_low_percent_format, with a notice about battery saver if on. [CHAR LIMIT=none]-->
<string name="battery_low_percent_format_saver_started"><xliff:g id="percentage">%s</xliff:g> remaining. Battery Saver is on.</string>
@@ -173,22 +188,25 @@
[CHAR LIMIT=25] -->
<string name="compat_mode_off">Stretch to fill screen</string>
+ <!-- Power menu item for taking a screenshot [CHAR LIMIT=20]-->
+ <string name="global_action_screenshot">Screenshot</string>
+
<!-- Notification ticker displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=30] -->
<string name="screenshot_saving_ticker">Saving screenshot\u2026</string>
<!-- Notification title displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=50] -->
<string name="screenshot_saving_title">Saving screenshot\u2026</string>
<!-- Notification text displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=100] -->
- <string name="screenshot_saving_text">Screenshot is being saved.</string>
+ <string name="screenshot_saving_text">Screenshot is being saved</string>
<!-- Notification title displayed when a screenshot is saved to the Gallery. [CHAR LIMIT=50] -->
- <string name="screenshot_saved_title">Screenshot captured.</string>
+ <string name="screenshot_saved_title">Screenshot saved</string>
<!-- Notification text displayed when a screenshot is saved to the Gallery. [CHAR LIMIT=100] -->
- <string name="screenshot_saved_text">Tap to view your screenshot.</string>
+ <string name="screenshot_saved_text">Tap to view your screenshot</string>
<!-- Notification title displayed when we fail to take a screenshot. [CHAR LIMIT=50] -->
- <string name="screenshot_failed_title">Couldn\'t capture screenshot.</string>
+ <string name="screenshot_failed_title">Couldn\'t capture screenshot</string>
<!-- Notification text displayed when we fail to save a screenshot for unknown reasons. [CHAR LIMIT=100] -->
- <string name="screenshot_failed_to_save_unknown_text">Problem encountered while saving screenshot.</string>
+ <string name="screenshot_failed_to_save_unknown_text">Problem encountered while saving screenshot</string>
<!-- Notification text displayed when we fail to save a screenshot. [CHAR LIMIT=100] -->
- <string name="screenshot_failed_to_save_text">Can\'t save screenshot due to limited storage space.</string>
+ <string name="screenshot_failed_to_save_text">Can\'t save screenshot due to limited storage space</string>
<!-- Notification text displayed when we fail to take a screenshot. [CHAR LIMIT=100] -->
<string name="screenshot_failed_to_capture_text">Taking screenshots isn\'t allowed by the app or
your organization</string>
@@ -1445,38 +1463,22 @@
<string name="notification_header_default_channel">Notifications</string>
<!-- Notification Inline Controls: Shown when a channel's notifications are currently blocked -->
- <string name="notification_channel_disabled">You won\'t get these notifications anymore</string>
+ <string name="notification_channel_disabled">You won\'t see these notifications anymore</string>
- <!-- Notification: Control panel: Label that shows how many channels are included in this bundle
- of notifications. Replaces the channel name and only appears when there is more than one channel. -->
- <string name="notification_num_channels"> <xliff:g id="number">%d</xliff:g> notification categories</string>
+ <!-- Notification Inline controls: continue receiving notifications prompt, channel level -->
+ <string name="inline_keep_showing">Keep showing these notifications?</string>
- <!-- Notification: Control panel: Label that shows when an app has not upgraded to use channels.
- Hints that the user's only option is to block all of the app's notifications. -->
- <string name="notification_default_channel_desc">This app doesn\'t have notification categories</string>
+ <!-- Notification inline controls: block notifications button -->
+ <string name="inline_stop_button">Stop notifications</string>
+
+ <!-- Notification inline controls: keep getting notifications button -->
+ <string name="inline_keep_button">Keep showing</string>
+
+ <!-- Notification Inline controls: continue receiving notifications prompt, app level -->
+ <string name="inline_keep_showing_app">Keep showing notifications from this app?</string>
<!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. -->
- <string name="notification_unblockable_desc">Notifications from this app can\'t be turned off</string>
-
- <!-- Notification: Control panel: Label that shows how many channels this application has
- defined, describing the current notification channel as "1 out of n notification categories from this app". -->
- <plurals name="notification_num_channels_desc">
- <item quantity="one">1 out of <xliff:g id="number">%s</xliff:g> notification category from this app</item>
- <item quantity="other">1 out of <xliff:g id="number">%s</xliff:g> notification categories from this app</item>
- </plurals>
-
- <!-- Notification: Control panel: For bundles of notifications, this label that lists the
- channels used by the contained notifications. The first two channels are listed by name,
- followed by "and N others"
- Example: "Friend requests, Friend confirmations"
- Example: "Friend requests, Friend confirmations, and 1 other"
- Example: "Friend requests, Friend confirmations, and 2 others"
- -->
- <string name="notification_channels_list_desc_2"><xliff:g id="channel_name_1">%1$s</xliff:g>, <xliff:g id="channel_name_2">%2$s</xliff:g></string>
- <plurals name="notification_channels_list_desc_2_and_others">
- <item quantity="one"><xliff:g id="channel_name_1">%1$s</xliff:g>, <xliff:g id="channel_name_2">%2$s</xliff:g>, and <xliff:g id="number">%3$d</xliff:g> other</item>
- <item quantity="other"><xliff:g id="channel_name_1">%1$s</xliff:g>, <xliff:g id="channel_name_2">%2$s</xliff:g>, and <xliff:g id="number">%3$d</xliff:g> others</item>
- </plurals>
+ <string name="notification_unblockable_desc">These notifications can\'t be turned off</string>
<!-- Notification: Control panel: Accessibility description for expanded inline controls view, used
to control settings about notifications related to the current notification. -->
@@ -1488,16 +1490,15 @@
or disable notifications from this channel -->
<string name="notification_channel_switch_accessibility">Allow notifications from this channel</string>
<!-- Notification: Control panel: Label for button that launches notification settings. Used
- when this app has defined more than a single channel for notifications. -->
- <string name="notification_all_categories">All Categories</string>
- <!-- Notification: Control panel: Label for button that launches notification settings. Used
when this app has only defined a single channel for notifications. -->
<string name="notification_more_settings">More settings</string>
<!-- Notification: Control panel: Label for a link that launches notification settings in the
app that sent the notification. -->
- <string name="notification_app_settings">Customize: <xliff:g id="sub_category" example="Work chats">%1$s</xliff:g></string>
+ <string name="notification_app_settings">Customize</string>
<!-- Notification: Control panel: Label for button that dismisses control panel. [CHAR LIMIT=NONE] -->
<string name="notification_done">Done</string>
+ <!-- Notification: inline controls: undo block button -->
+ <string name="inline_undo">Undo</string>
<!-- Notification: Menu row: Content description for menu items. [CHAR LIMIT=NONE] -->
<string name="notification_menu_accessibility"><xliff:g id="app_name" example="YouTube">%1$s</xliff:g> <xliff:g id="menu_description" example="notification controls">%2$s</xliff:g></string>
@@ -2055,4 +2056,5 @@
<string name="touch_filtered_warning">Because an app is obscuring a permission request, Settings
can’t verify your response.</string>
+
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 173a90a..64fa9c6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -22,4 +22,8 @@
oneway interface IOverviewProxy {
void onBind(in ISystemUiProxy sysUiProxy);
void onMotionEvent(in MotionEvent event);
+ void onQuickSwitch();
+ void onQuickScrubStart();
+ void onQuickScrubEnd();
+ void onQuickScrubProgress(float progress);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index a5d1963..13f30b2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -28,11 +28,14 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
import android.os.Trace;
import android.util.ArraySet;
import android.util.IntProperty;
import android.util.Property;
import android.util.TypedValue;
+import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
@@ -291,6 +294,20 @@
}
/**
+ * @return The next frame name for the specified surface.
+ */
+ public static long getNextFrameNumber(Surface s) {
+ return s.getNextFrameNumber();
+ }
+
+ /**
+ * @return The surface for the specified view.
+ */
+ public static Surface getSurface(View v) {
+ return v.getViewRootImpl().mSurface;
+ }
+
+ /**
* Returns a lightweight dump of a rect.
*/
public static String dumpRect(Rect r) {
@@ -299,4 +316,12 @@
}
return r.left + "," + r.top + "-" + r.right + "," + r.bottom;
}
+
+ /**
+ * Posts a runnable on a handler at the front of the queue ignoring any sync barriers.
+ */
+ public static void postAtFrontOfQueueAsynchronously(Handler h, Runnable r) {
+ Message msg = h.obtainMessage().setCallback(r);
+ h.sendMessageAtFrontOfQueue(msg);
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java
new file mode 100644
index 0000000..0d8ce58
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.app.Activity;
+
+public class ActivityCompat {
+ private final Activity mWrapped;
+
+ public ActivityCompat(Activity activity) {
+ mWrapped = activity;
+ }
+
+ /**
+ * @see Activity#registerRemoteAnimations
+ */
+ public void registerRemoteAnimations(RemoteAnimationDefinitionCompat definition) {
+ mWrapped.registerRemoteAnimations(definition.getWrapped());
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index 705a215..712cca6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -38,4 +38,9 @@
: SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT);
return options;
}
+
+ public static ActivityOptions makeRemoteAnimation(
+ RemoteAnimationAdapterCompat remoteAnimationAdapter) {
+ return ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter.getWrapped());
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
new file mode 100644
index 0000000..625b1de
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
+
+/**
+ * @see RemoteAnimationAdapter
+ */
+public class RemoteAnimationAdapterCompat {
+
+ private final RemoteAnimationAdapter mWrapped;
+
+ public RemoteAnimationAdapterCompat(RemoteAnimationRunnerCompat runner, long duration,
+ long statusBarTransitionDelay) {
+ mWrapped = new RemoteAnimationAdapter(wrapRemoteAnimationRunner(runner), duration,
+ statusBarTransitionDelay);
+ }
+
+ RemoteAnimationAdapter getWrapped() {
+ return mWrapped;
+ }
+
+ private static IRemoteAnimationRunner.Stub wrapRemoteAnimationRunner(
+ RemoteAnimationRunnerCompat remoteAnimationAdapter) {
+ return new IRemoteAnimationRunner.Stub() {
+ @Override
+ public void onAnimationStart(RemoteAnimationTarget[] apps,
+ IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
+ final RemoteAnimationTargetCompat[] appsCompat =
+ RemoteAnimationTargetCompat.wrap(apps);
+ final Runnable animationFinishedCallback = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Log.e("ActivityOptionsCompat", "Failed to call app controlled animation"
+ + " finished callback", e);
+ }
+ }
+ };
+ remoteAnimationAdapter.onAnimationStart(appsCompat, animationFinishedCallback);
+ }
+
+ @Override
+ public void onAnimationCancelled() throws RemoteException {
+ remoteAnimationAdapter.onAnimationCancelled();
+ }
+ };
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationDefinitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationDefinitionCompat.java
new file mode 100644
index 0000000..5fff5fe
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationDefinitionCompat.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.view.RemoteAnimationDefinition;
+
+/**
+ * @see RemoteAnimationDefinition
+ */
+public class RemoteAnimationDefinitionCompat {
+
+ private final RemoteAnimationDefinition mWrapped = new RemoteAnimationDefinition();
+
+ public void addRemoteAnimation(int transition, RemoteAnimationAdapterCompat adapter) {
+ mWrapped.addRemoteAnimation(transition, adapter.getWrapped());
+ }
+
+ RemoteAnimationDefinition getWrapped() {
+ return mWrapped;
+ }
+}
diff --git a/core/java/android/security/recoverablekeystore/KeyDerivationParameters.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
similarity index 63%
copy from core/java/android/security/recoverablekeystore/KeyDerivationParameters.aidl
copy to packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
index fe13179..5a85df9 100644
--- a/core/java/android/security/recoverablekeystore/KeyDerivationParameters.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -11,10 +11,12 @@
* 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.
+ * limitations under the License
*/
-package android.security.recoverablekeystore;
+package com.android.systemui.shared.system;
-/* @hide */
-parcelable KeyDerivationParameters;
+public interface RemoteAnimationRunnerCompat {
+ void onAnimationStart(RemoteAnimationTargetCompat[] apps, Runnable finishedCallback);
+ void onAnimationCancelled();
+}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
new file mode 100644
index 0000000..3871980
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.RemoteAnimationTarget;
+
+/**
+ * @see RemoteAnimationTarget
+ */
+public class RemoteAnimationTargetCompat {
+
+ public static final int MODE_OPENING = RemoteAnimationTarget.MODE_OPENING;
+ public static final int MODE_CLOSING = RemoteAnimationTarget.MODE_CLOSING;
+
+ public final int taskId;
+ public final int mode;
+ public final SurfaceControlCompat leash;
+ public final boolean isTranslucent;
+ public final Rect clipRect;
+ public final int prefixOrderIndex;
+ public final Point position;
+ public final Rect sourceContainerBounds;
+
+ public RemoteAnimationTargetCompat(RemoteAnimationTarget app) {
+ taskId = app.taskId;
+ mode = app.mode;
+ leash = new SurfaceControlCompat(app.leash);
+ isTranslucent = app.isTranslucent;
+ clipRect = app.clipRect;
+ position = app.position;
+ sourceContainerBounds = app.sourceContainerBounds;
+ prefixOrderIndex = app.prefixOrderIndex;
+ }
+
+ public static RemoteAnimationTargetCompat[] wrap(RemoteAnimationTarget[] apps) {
+ final RemoteAnimationTargetCompat[] appsCompat =
+ new RemoteAnimationTargetCompat[apps.length];
+ for (int i = 0; i < apps.length; i++) {
+ appsCompat[i] = new RemoteAnimationTargetCompat(apps[i]);
+ }
+ return appsCompat;
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceControlCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceControlCompat.java
new file mode 100644
index 0000000..cd12141
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceControlCompat.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.view.SurfaceControl;
+
+public class SurfaceControlCompat {
+ SurfaceControl mSurfaceControl;
+
+ public SurfaceControlCompat(SurfaceControl surfaceControl) {
+ mSurfaceControl = surfaceControl;
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
new file mode 100644
index 0000000..c82c519
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+
+public class TransactionCompat {
+
+ private final Transaction mTransaction;
+
+ private final float[] mTmpValues = new float[9];
+
+ public TransactionCompat() {
+ mTransaction = new Transaction();
+ }
+
+ public void apply() {
+ mTransaction.apply();
+ }
+
+ public TransactionCompat show(SurfaceControlCompat surfaceControl) {
+ mTransaction.show(surfaceControl.mSurfaceControl);
+ return this;
+ }
+
+ public TransactionCompat hide(SurfaceControlCompat surfaceControl) {
+ mTransaction.hide(surfaceControl.mSurfaceControl);
+ return this;
+ }
+
+ public TransactionCompat setPosition(SurfaceControlCompat surfaceControl, float x, float y) {
+ mTransaction.setPosition(surfaceControl.mSurfaceControl, x, y);
+ return this;
+ }
+
+ public TransactionCompat setSize(SurfaceControlCompat surfaceControl, int w, int h) {
+ mTransaction.setSize(surfaceControl.mSurfaceControl, w, h);
+ return this;
+ }
+
+ public TransactionCompat setLayer(SurfaceControlCompat surfaceControl, int z) {
+ mTransaction.setLayer(surfaceControl.mSurfaceControl, z);
+ return this;
+ }
+
+ public TransactionCompat setAlpha(SurfaceControlCompat surfaceControl, float alpha) {
+ mTransaction.setAlpha(surfaceControl.mSurfaceControl, alpha);
+ return this;
+ }
+
+ public TransactionCompat setMatrix(SurfaceControlCompat surfaceControl, float dsdx, float dtdx,
+ float dtdy, float dsdy) {
+ mTransaction.setMatrix(surfaceControl.mSurfaceControl, dsdx, dtdx, dtdy, dsdy);
+ return this;
+ }
+
+ public TransactionCompat setMatrix(SurfaceControlCompat surfaceControl, Matrix matrix) {
+ mTransaction.setMatrix(surfaceControl.mSurfaceControl, matrix, mTmpValues);
+ return this;
+ }
+
+ public TransactionCompat setWindowCrop(SurfaceControlCompat surfaceControl, Rect crop) {
+ mTransaction.setWindowCrop(surfaceControl.mSurfaceControl, crop);
+ return this;
+ }
+
+ public TransactionCompat setFinalCrop(SurfaceControlCompat surfaceControl, Rect crop) {
+ mTransaction.setFinalCrop(surfaceControl.mSurfaceControl, crop);
+ return this;
+ }
+
+ public TransactionCompat deferTransactionUntil(SurfaceControlCompat surfaceControl,
+ IBinder handle, long frameNumber) {
+ mTransaction.deferTransactionUntil(surfaceControl.mSurfaceControl, handle, frameNumber);
+ return this;
+ }
+
+ public TransactionCompat deferTransactionUntil(SurfaceControlCompat surfaceControl,
+ Surface barrier, long frameNumber) {
+ mTransaction.deferTransactionUntilSurface(surfaceControl.mSurfaceControl, barrier,
+ frameNumber);
+ return this;
+ }
+
+ public TransactionCompat setColor(SurfaceControlCompat surfaceControl, float[] color) {
+ mTransaction.setColor(surfaceControl.mSurfaceControl, color);
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 225dbb4..68400fc 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -20,9 +20,10 @@
import android.graphics.Rect;
import android.os.Handler;
-import android.os.IRemoteCallback;
import android.os.RemoteException;
import android.util.Log;
+import android.view.RemoteAnimationAdapter;
+import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
@@ -32,6 +33,31 @@
private static final String TAG = "WindowManagerWrapper";
+ public static final int TRANSIT_UNSET = WindowManager.TRANSIT_UNSET;
+ public static final int TRANSIT_NONE = WindowManager.TRANSIT_NONE;
+ public static final int TRANSIT_ACTIVITY_OPEN = WindowManager.TRANSIT_ACTIVITY_OPEN;
+ public static final int TRANSIT_ACTIVITY_CLOSE = WindowManager.TRANSIT_ACTIVITY_CLOSE;
+ public static final int TRANSIT_TASK_OPEN = WindowManager.TRANSIT_TASK_OPEN;
+ public static final int TRANSIT_TASK_CLOSE = WindowManager.TRANSIT_TASK_CLOSE;
+ public static final int TRANSIT_TASK_TO_FRONT = WindowManager.TRANSIT_TASK_TO_FRONT;
+ public static final int TRANSIT_TASK_TO_BACK = WindowManager.TRANSIT_TASK_TO_BACK;
+ public static final int TRANSIT_WALLPAPER_CLOSE = WindowManager.TRANSIT_WALLPAPER_CLOSE;
+ public static final int TRANSIT_WALLPAPER_OPEN = WindowManager.TRANSIT_WALLPAPER_OPEN;
+ public static final int TRANSIT_WALLPAPER_INTRA_OPEN =
+ WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN;
+ public static final int TRANSIT_WALLPAPER_INTRA_CLOSE =
+ WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE;
+ public static final int TRANSIT_TASK_OPEN_BEHIND = WindowManager.TRANSIT_TASK_OPEN_BEHIND;
+ public static final int TRANSIT_TASK_IN_PLACE = WindowManager.TRANSIT_TASK_IN_PLACE;
+ public static final int TRANSIT_ACTIVITY_RELAUNCH = WindowManager.TRANSIT_ACTIVITY_RELAUNCH;
+ public static final int TRANSIT_DOCK_TASK_FROM_RECENTS =
+ WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
+ public static final int TRANSIT_KEYGUARD_GOING_AWAY = WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+ public static final int TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER =
+ WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+ public static final int TRANSIT_KEYGUARD_OCCLUDE = WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
+ public static final int TRANSIT_KEYGUARD_UNOCCLUDE = WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+
private static final WindowManagerWrapper sInstance = new WindowManagerWrapper();
public static WindowManagerWrapper getInstance() {
@@ -65,4 +91,14 @@
Log.w(TAG, "Failed to override pending app transition (multi-thumbnail future): ", e);
}
}
+
+ public void overridePendingAppTransitionRemote(
+ RemoteAnimationAdapterCompat remoteAnimationAdapter) {
+ try {
+ WindowManagerGlobal.getWindowManagerService().overridePendingAppTransitionRemote(
+ remoteAnimationAdapter.getWrapped());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to override pending app transition (remote): ", e);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index a980413..d63ad08 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -280,7 +280,7 @@
@Override
public void showPromptReason(int reason) {
if (reason != PROMPT_REASON_NONE) {
- int promtReasonStringRes = getPromtReasonStringRes(reason);
+ int promtReasonStringRes = getPromptReasonStringRes(reason);
if (promtReasonStringRes != 0) {
mSecurityMessageDisplay.setMessage(promtReasonStringRes);
}
@@ -288,12 +288,12 @@
}
@Override
- public void showMessage(String message, int color) {
+ public void showMessage(CharSequence message, int color) {
mSecurityMessageDisplay.setNextMessageColor(color);
mSecurityMessageDisplay.setMessage(message);
}
- protected abstract int getPromtReasonStringRes(int reason);
+ protected abstract int getPromptReasonStringRes(int reason);
// Cause a VIRTUAL_KEY vibration
public void doHapticKeyClick() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
index 27a3f7d..f1a5ca9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
@@ -34,6 +34,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.settingslib.Utils;
import java.io.File;
@@ -171,10 +172,14 @@
mSecurityContainer.showPromptReason(reason);
}
- public void showMessage(String message, int color) {
+ public void showMessage(CharSequence message, int color) {
mSecurityContainer.showMessage(message, color);
}
+ public void showErrorMessage(CharSequence message) {
+ showMessage(message, Utils.getColorError(mContext));
+ }
+
/**
* Dismisses the keyguard by going to the next screen or making it gone.
* @param targetUserId a user that needs to be the foreground user at the dismissal completion.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index b6184a8..ff5f5e7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -117,7 +117,7 @@
}
@Override
- protected int getPromtReasonStringRes(int reason) {
+ protected int getPromptReasonStringRes(int reason) {
switch (reason) {
case PROMPT_REASON_RESTART:
return R.string.kg_prompt_reason_restart_password;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index d636316..cb066a1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -398,7 +398,7 @@
}
@Override
- public void showMessage(String message, int color) {
+ public void showMessage(CharSequence message, int color) {
mSecurityMessageDisplay.setNextMessageColor(color);
mSecurityMessageDisplay.setMessage(message);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index c04ae68..6539ccf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -103,7 +103,7 @@
}
@Override
- protected int getPromtReasonStringRes(int reason) {
+ protected int getPromptReasonStringRes(int reason) {
switch (reason) {
case PROMPT_REASON_RESTART:
return R.string.kg_prompt_reason_restart_pin;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 9f39321..8dc4609 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -543,8 +543,7 @@
}
}
-
- public void showMessage(String message, int color) {
+ public void showMessage(CharSequence message, int color) {
if (mCurrentSecuritySelection != SecurityMode.None) {
getSecurityView(mCurrentSecuritySelection).showMessage(message, color);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
index 8290842..360dba3b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
@@ -106,7 +106,7 @@
* @param message the message to show
* @param color the color to use
*/
- void showMessage(String message, int color);
+ void showMessage(CharSequence message, int color);
/**
* Instruct the view to show usability hints, if any.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index 6012c45..a2ff8f7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -139,7 +139,7 @@
}
@Override
- public void showMessage(String message, int color) {
+ public void showMessage(CharSequence message, int color) {
KeyguardSecurityView ksv = getSecurityView();
if (ksv != null) {
ksv.showMessage(message, color);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index 6e0b56e2..e7432ba 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -168,7 +168,7 @@
}
@Override
- protected int getPromtReasonStringRes(int reason) {
+ protected int getPromptReasonStringRes(int reason) {
// No message on SIM Pin
return 0;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index 876d170..afee8ec 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -211,7 +211,7 @@
}
@Override
- protected int getPromtReasonStringRes(int reason) {
+ protected int getPromptReasonStringRes(int reason) {
// No message on SIM Puk
return 0;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index 8135c61..d80a336 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -151,6 +151,7 @@
mClickActions.clear();
final int subItemsCount = subItems.size();
+ final int blendedColor = getTextColor();
for (int i = 0; i < subItemsCount; i++) {
SliceItem item = subItems.get(i);
@@ -159,7 +160,7 @@
KeyguardSliceButton button = mRow.findViewWithTag(itemTag);
if (button == null) {
button = new KeyguardSliceButton(mContext);
- button.setTextColor(mTextColor);
+ button.setTextColor(blendedColor);
button.setTag(itemTag);
} else {
mRow.removeView(button);
@@ -258,7 +259,7 @@
}
private void updateTextColors() {
- final int blendedColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount);
+ final int blendedColor = getTextColor();
mTitle.setTextColor(blendedColor);
int childCount = mRow.getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -322,6 +323,10 @@
}
}
+ public int getTextColor() {
+ return ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount);
+ }
+
/**
* Representation of an item that appears under the clock on main keyguard message.
* Shows optional separator.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 4b9a874..2873afb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -16,7 +16,6 @@
package com.android.keyguard;
-import android.app.ActivityManager;
import android.app.AlarmManager;
import android.content.Context;
import android.content.res.Configuration;
@@ -40,7 +39,6 @@
import android.widget.TextView;
import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.ChargingView;
import com.google.android.collect.Sets;
@@ -60,7 +58,6 @@
private View mClockSeparator;
private TextView mOwnerInfo;
private ViewGroup mClockContainer;
- private ChargingView mBatteryDoze;
private KeyguardSliceView mKeyguardSlice;
private Runnable mPendingMarqueeStart;
private Handler mHandler;
@@ -155,11 +152,9 @@
mClockView.setAccessibilityDelegate(new KeyguardClockAccessibilityDelegate(mContext));
}
mOwnerInfo = findViewById(R.id.owner_info);
- mBatteryDoze = findViewById(R.id.battery_doze);
mKeyguardSlice = findViewById(R.id.keyguard_status_area);
mClockSeparator = findViewById(R.id.clock_separator);
- mVisibleInDoze = Sets.newArraySet(mBatteryDoze, mClockView, mKeyguardSlice,
- mClockSeparator);
+ mVisibleInDoze = Sets.newArraySet(mClockView, mKeyguardSlice, mClockSeparator);
mTextColor = mClockView.getCurrentTextColor();
mKeyguardSlice.setListener(this::onSliceContentChanged);
@@ -184,10 +179,6 @@
mClockView.setTranslationY(translation);
mClockView.setScaleX(clockScale);
mClockView.setScaleY(clockScale);
- final float batteryTranslation =
- -(mClockView.getWidth() - (mClockView.getWidth() * clockScale)) / 2;
- mBatteryDoze.setTranslationX(batteryTranslation);
- mBatteryDoze.setTranslationY(translation);
mClockSeparator.setVisibility(hasHeader ? VISIBLE : GONE);
}
@@ -310,7 +301,7 @@
}
}
- public void setDark(float darkAmount) {
+ public void setDarkAmount(float darkAmount) {
if (mDarkAmount == darkAmount) {
return;
}
@@ -331,7 +322,6 @@
final int blendedTextColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, darkAmount);
updateDozeVisibleViews();
- mBatteryDoze.setDark(dark);
mKeyguardSlice.setDark(darkAmount);
mClockView.setTextColor(blendedTextColor);
mClockSeparator.setBackgroundColor(blendedTextColor);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 2058f15..e58ad05 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -372,6 +372,11 @@
}
}
+ @Override
+ public void onTrustError(CharSequence message) {
+ dispatchErrorMessage(message);
+ }
+
protected void handleSimSubscriptionInfoChanged() {
if (DEBUG_SIM_STATES) {
Log.v(TAG, "onSubscriptionInfoChanged()");
@@ -706,6 +711,15 @@
return mScreenOn;
}
+ private void dispatchErrorMessage(CharSequence message) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onTrustAgentErrorMessage(message);
+ }
+ }
+ }
+
static class DisplayClientState {
public int clientGeneration;
public boolean clearing;
@@ -1599,11 +1613,10 @@
}
}
- private static boolean isBatteryUpdateInteresting(BatteryStatus old, BatteryStatus current) {
+ private boolean isBatteryUpdateInteresting(BatteryStatus old, BatteryStatus current) {
final boolean nowPluggedIn = current.isPluggedIn();
final boolean wasPluggedIn = old.isPluggedIn();
- final boolean stateChangedWhilePluggedIn =
- wasPluggedIn == true && nowPluggedIn == true
+ final boolean stateChangedWhilePluggedIn = wasPluggedIn && nowPluggedIn
&& (old.status != current.status);
// change in plug state is always interesting
@@ -1616,6 +1629,11 @@
return true;
}
+ // change in battery level while keyguard visible
+ if (mKeyguardIsVisible && old.level != current.level) {
+ return true;
+ }
+
// change where battery needs charging
if (!nowPluggedIn && current.isBatteryLow() && current.level != old.level) {
return true;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 8d55eea..1afcca6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -17,13 +17,14 @@
import android.app.admin.DevicePolicyManager;
import android.graphics.Bitmap;
+import android.hardware.fingerprint.FingerprintManager;
import android.media.AudioManager;
import android.os.SystemClock;
-import android.hardware.fingerprint.FingerprintManager;
import android.telephony.TelephonyManager;
import android.view.WindowManagerPolicyConstants;
import com.android.internal.telephony.IccCardConstants;
+import com.android.systemui.statusbar.KeyguardIndicationController;
/**
* Callback for general information relevant to lock screen.
@@ -271,4 +272,15 @@
* @param dreaming true if the dream's window has been created and is visible
*/
public void onDreamingStateChanged(boolean dreaming) { }
+
+ /**
+ * Called when an error message needs to be presented on the keyguard.
+ * Message will be visible briefly, and might be overridden by other keyguard events,
+ * like fingerprint authentication errors.
+ *
+ * @param message Message that indicates an error.
+ * @see KeyguardIndicationController.BaseKeyguardCallback#HIDE_DELAY_MS
+ * @see KeyguardIndicationController#showTransientIndication(CharSequence)
+ */
+ public void onTrustAgentErrorMessage(CharSequence message) { }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
index eff84c6..5c68123 100644
--- a/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
@@ -99,4 +99,10 @@
* Invoked when the secondary display showing a keyguard window changes.
*/
void onSecondaryDisplayShowingChanged(int displayId);
+
+ /**
+ * Consumes a message that was enqueued to be displayed on the next time the bouncer shows up.
+ * @return Message that should be displayed above the challenge.
+ */
+ CharSequence consumeCustomMessage();
}
diff --git a/packages/SystemUI/src/com/android/systemui/ChargingView.java b/packages/SystemUI/src/com/android/systemui/ChargingView.java
deleted file mode 100644
index 33f8b06..0000000
--- a/packages/SystemUI/src/com/android/systemui/ChargingView.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2017 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;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.os.UserHandle;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-
-import com.android.internal.hardware.AmbientDisplayConfiguration;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-
-/**
- * A view that only shows its drawable while the phone is charging.
- *
- * Also reloads its drawable upon density changes.
- */
-public class ChargingView extends ImageView implements
- BatteryController.BatteryStateChangeCallback,
- ConfigurationController.ConfigurationListener {
-
- private static final long CHARGING_INDICATION_DELAY_MS = 1000;
-
- private final AmbientDisplayConfiguration mConfig;
- private final Runnable mClearSuppressCharging = this::clearSuppressCharging;
- private BatteryController mBatteryController;
- private int mImageResource;
- private boolean mCharging;
- private boolean mDark;
- private boolean mSuppressCharging;
-
-
- private void clearSuppressCharging() {
- mSuppressCharging = false;
- removeCallbacks(mClearSuppressCharging);
- updateVisibility();
- }
-
- public ChargingView(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
-
- mConfig = new AmbientDisplayConfiguration(context);
-
- TypedArray a = context.obtainStyledAttributes(attrs, new int[]{android.R.attr.src});
- int srcResId = a.getResourceId(0, 0);
-
- if (srcResId != 0) {
- mImageResource = srcResId;
- }
-
- a.recycle();
-
- updateVisibility();
- }
-
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- mBatteryController = Dependency.get(BatteryController.class);
- mBatteryController.addCallback(this);
- Dependency.get(ConfigurationController.class).addCallback(this);
- }
-
- @Override
- public void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mBatteryController.removeCallback(this);
- Dependency.get(ConfigurationController.class).removeCallback(this);
- removeCallbacks(mClearSuppressCharging);
- }
-
- @Override
- public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
- boolean startCharging = charging && !mCharging;
- if (startCharging && deviceWillWakeUpWhenPluggedIn() && mDark) {
- // We're about to wake up, and thus don't want to show the indicator just for it to be
- // hidden again.
- clearSuppressCharging();
- mSuppressCharging = true;
- postDelayed(mClearSuppressCharging, CHARGING_INDICATION_DELAY_MS);
- }
- mCharging = charging;
- updateVisibility();
- }
-
- private boolean deviceWillWakeUpWhenPluggedIn() {
- boolean plugTurnsOnScreen = getResources().getBoolean(
- com.android.internal.R.bool.config_unplugTurnsOnScreen);
- boolean aod = mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT);
- return !aod && plugTurnsOnScreen;
- }
-
- @Override
- public void onDensityOrFontScaleChanged() {
- setImageResource(mImageResource);
- }
-
- public void setDark(boolean dark) {
- mDark = dark;
- if (!dark) {
- clearSuppressCharging();
- }
- updateVisibility();
- }
-
- private void updateVisibility() {
- setVisibility(mCharging && !mSuppressCharging && mDark ? VISIBLE : INVISIBLE);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index e7e70af..7403ddc 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -40,6 +40,8 @@
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.PluginManagerImpl;
import com.android.systemui.plugins.VolumeDialogController;
+import com.android.systemui.power.EnhancedEstimates;
+import com.android.systemui.power.EnhancedEstimatesImpl;
import com.android.systemui.power.PowerNotificationWarnings;
import com.android.systemui.power.PowerUI;
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
@@ -310,6 +312,8 @@
mProviders.put(OverviewProxyService.class, () -> new OverviewProxyService(mContext));
+ mProviders.put(EnhancedEstimates.class, () -> new EnhancedEstimatesImpl());
+
// Put all dependencies above here so the factory can override them if it wants.
SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
}
diff --git a/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java b/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java
index 6aa465c..5d2e4d0 100644
--- a/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java
+++ b/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java
@@ -16,19 +16,14 @@
package com.android.systemui;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
import android.content.Context;
-import android.database.ContentObserver;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
-import android.graphics.Point;
-import android.graphics.Region;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.provider.Settings;
import android.view.DisplayCutout;
import android.view.Gravity;
import android.view.View;
@@ -37,25 +32,35 @@
import android.view.WindowInsets;
import android.view.WindowManager;
-import java.util.Collections;
-import java.util.List;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
/**
* Emulates a display cutout by drawing its shape in an overlay as supplied by
* {@link DisplayCutout}.
*/
-public class EmulatedDisplayCutout extends SystemUI {
+public class EmulatedDisplayCutout extends SystemUI implements ConfigurationListener {
private View mOverlay;
private boolean mAttached;
private WindowManager mWindowManager;
@Override
public void start() {
+ Dependency.get(ConfigurationController.class).addCallback(this);
+
mWindowManager = mContext.getSystemService(WindowManager.class);
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.EMULATE_DISPLAY_CUTOUT),
- false, mObserver, UserHandle.USER_ALL);
- mObserver.onChange(false);
+ updateAttached();
+ }
+
+ @Override
+ public void onOverlayChanged() {
+ updateAttached();
+ }
+
+ private void updateAttached() {
+ boolean shouldAttach = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout);
+ setAttached(shouldAttach);
}
private void setAttached(boolean attached) {
@@ -87,23 +92,12 @@
PixelFormat.TRANSLUCENT);
lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS
| WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
- lp.flags2 |= WindowManager.LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA;
+ lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
lp.setTitle("EmulatedDisplayCutout");
lp.gravity = Gravity.TOP;
return lp;
}
- private ContentObserver mObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
- @Override
- public void onChange(boolean selfChange) {
- boolean emulateCutout = Settings.Global.getInt(
- mContext.getContentResolver(), Settings.Global.EMULATE_DISPLAY_CUTOUT,
- Settings.Global.EMULATE_DISPLAY_CUTOUT_OFF)
- != Settings.Global.EMULATE_DISPLAY_CUTOUT_OFF;
- setAttached(emulateCutout);
- }
- };
-
private static class CutoutView extends View {
private final Paint mPaint = new Paint();
private final Path mBounds = new Path();
@@ -114,10 +108,9 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ mBounds.reset();
if (insets.getDisplayCutout() != null) {
insets.getDisplayCutout().getBounds().getBoundaryPath(mBounds);
- } else {
- mBounds.reset();
}
invalidate();
return insets.consumeDisplayCutout();
@@ -126,7 +119,7 @@
@Override
protected void onDraw(Canvas canvas) {
if (!mBounds.isEmpty()) {
- mPaint.setColor(Color.DKGRAY);
+ mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawPath(mBounds, mPaint);
diff --git a/packages/SystemUI/src/com/android/systemui/RoundedCorners.java b/packages/SystemUI/src/com/android/systemui/RoundedCorners.java
index b15b79f..c960fa1 100644
--- a/packages/SystemUI/src/com/android/systemui/RoundedCorners.java
+++ b/packages/SystemUI/src/com/android/systemui/RoundedCorners.java
@@ -14,6 +14,8 @@
package com.android.systemui;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
import static com.android.systemui.tuner.TunablePadding.FLAG_START;
import static com.android.systemui.tuner.TunablePadding.FLAG_END;
@@ -163,6 +165,7 @@
| WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
lp.setTitle("RoundedOverlay");
lp.gravity = Gravity.TOP;
+ lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
return lp;
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index e008148..0f34513 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -24,10 +24,12 @@
import android.app.WallpaperManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.ServiceConnection;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.graphics.Point;
@@ -36,7 +38,9 @@
import android.net.ConnectivityManager;
import android.os.Build;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Message;
+import android.os.Messenger;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
@@ -77,6 +81,7 @@
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.util.EmergencyAffordanceManager;
+import com.android.internal.util.ScreenshotHelper;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Dependency;
import com.android.systemui.HardwareUiLayout;
@@ -117,6 +122,7 @@
private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
private static final String GLOBAL_ACTION_KEY_RESTART = "restart";
private static final String GLOBAL_ACTION_KEY_LOGOUT = "logout";
+ private static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot";
private final Context mContext;
private final GlobalActionsManager mWindowManagerFuncs;
@@ -143,6 +149,7 @@
private boolean mHasLogoutButton;
private final boolean mShowSilentToggle;
private final EmergencyAffordanceManager mEmergencyAffordanceManager;
+ private final ScreenshotHelper mScreenshotHelper;
/**
* @param context everything needs a context :(
@@ -183,6 +190,7 @@
R.bool.config_useFixedVolume);
mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
+ mScreenshotHelper = new ScreenshotHelper(context);
}
/**
@@ -340,6 +348,8 @@
mItems.add(getAssistAction());
} else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
mItems.add(new RestartAction());
+ } else if (GLOBAL_ACTION_KEY_SCREENSHOT.equals(actionKey)) {
+ mItems.add(new ScreenshotAction());
} else if (GLOBAL_ACTION_KEY_LOGOUT.equals(actionKey)) {
if (mDevicePolicyManager.isLogoutEnabled()
&& getCurrentUser().id != UserHandle.USER_SYSTEM) {
@@ -458,6 +468,38 @@
}
+ private class ScreenshotAction extends SinglePressAction {
+ public ScreenshotAction() {
+ super(R.drawable.ic_screenshot, R.string.global_action_screenshot);
+ }
+
+ @Override
+ public void onPress() {
+ // Add a little delay before executing, to give the
+ // dialog a chance to go away before it takes a
+ // screenshot.
+ // TODO: instead, omit global action dialog layer
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ mScreenshotHelper.takeScreenshot(1, true, true, mHandler);
+ MetricsLogger.action(mContext,
+ MetricsEvent.ACTION_SCREENSHOT_POWER_MENU);
+ }
+ }, 500);
+ }
+
+ @Override
+ public boolean showDuringKeyguard() {
+ return true;
+ }
+
+ @Override
+ public boolean showBeforeProvisioning() {
+ return false;
+ }
+ }
+
private class BugReportAction extends SinglePressAction implements LongPressAction {
public BugReportAction() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 2a5ae0d..22b41a4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -96,9 +96,9 @@
}
@Override // Binder interface
- public void dismiss(IKeyguardDismissCallback callback) {
+ public void dismiss(IKeyguardDismissCallback callback, CharSequence message) {
checkPermission();
- mKeyguardViewMediator.dismiss(callback);
+ mKeyguardViewMediator.dismiss(callback, message);
}
@Override // Binder interface
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index bd46c5f..e49e80d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -103,11 +103,12 @@
@Override
public Slice onBindSlice(Uri sliceUri) {
- ListBuilder builder = new ListBuilder(mSliceUri)
- .addRow(new RowBuilder(mDateUri).setTitle(mLastText));
+ ListBuilder builder = new ListBuilder(getContext(), mSliceUri);
+ builder.addRow(new RowBuilder(builder, mDateUri).setTitle(mLastText));
if (!TextUtils.isEmpty(mNextAlarm)) {
Icon icon = Icon.createWithResource(getContext(), R.drawable.ic_access_alarms_big);
- builder.addRow(new RowBuilder(mAlarmUri).setTitle(mNextAlarm).addEndItem(icon));
+ builder.addRow(new RowBuilder(builder, mAlarmUri)
+ .setTitle(mNextAlarm).addEndItem(icon));
}
return builder.build();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 91ae448..653e500 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -25,7 +25,6 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
-
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.NotificationManager;
@@ -344,6 +343,7 @@
private boolean mWakeAndUnlocking;
private IKeyguardDrawnCallback mDrawnCallback;
private boolean mLockWhenSimRemoved;
+ private CharSequence mCustomMessage;
KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
@@ -368,7 +368,7 @@
return;
} else if (info.isGuest() || info.isDemo()) {
// If we just switched to a guest, try to dismiss keyguard.
- dismiss(null /* callback */);
+ dismiss(null /* callback */, null /* message */);
}
}
}
@@ -654,6 +654,13 @@
}
@Override
+ public CharSequence consumeCustomMessage() {
+ final CharSequence message = mCustomMessage;
+ mCustomMessage = null;
+ return message;
+ }
+
+ @Override
public void onSecondaryDisplayShowingChanged(int displayId) {
synchronized (KeyguardViewMediator.this) {
setShowingLocked(mShowing, displayId, false);
@@ -1321,20 +1328,22 @@
/**
* Dismiss the keyguard through the security layers.
* @param callback Callback to be informed about the result
+ * @param message Message that should be displayed on the bouncer.
*/
- private void handleDismiss(IKeyguardDismissCallback callback) {
+ private void handleDismiss(IKeyguardDismissCallback callback, CharSequence message) {
if (mShowing) {
if (callback != null) {
mDismissCallbackRegistry.addCallback(callback);
}
+ mCustomMessage = message;
mStatusBarKeyguardViewManager.dismissAndCollapse();
} else if (callback != null) {
new DismissCallbackWrapper(callback).notifyDismissError();
}
}
- public void dismiss(IKeyguardDismissCallback callback) {
- mHandler.obtainMessage(DISMISS, callback).sendToTarget();
+ public void dismiss(IKeyguardDismissCallback callback, CharSequence message) {
+ mHandler.obtainMessage(DISMISS, new DismissMessage(callback, message)).sendToTarget();
}
/**
@@ -1551,7 +1560,8 @@
}
break;
case DISMISS:
- handleDismiss((IKeyguardDismissCallback) msg.obj);
+ final DismissMessage message = (DismissMessage) msg.obj;
+ handleDismiss(message.getCallback(), message.getMessage());
break;
case START_KEYGUARD_EXIT_ANIM:
Trace.beginSection("KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM");
@@ -2161,4 +2171,22 @@
}
}
}
+
+ private static class DismissMessage {
+ private final CharSequence mMessage;
+ private final IKeyguardDismissCallback mCallback;
+
+ DismissMessage(IKeyguardDismissCallback callback, CharSequence message) {
+ mCallback = callback;
+ mMessage = message;
+ }
+
+ public IKeyguardDismissCallback getCallback() {
+ return mCallback;
+ }
+
+ public CharSequence getMessage() {
+ return mMessage;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index bfe07a9..0486a9d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -373,7 +373,7 @@
if (menuState == MENU_STATE_FULL) {
mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim);
} else {
- mMenuContainerAnimator.playTogether(settingsAnim, dismissAnim);
+ mMenuContainerAnimator.playTogether(dismissAnim);
}
mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
mMenuContainerAnimator.setDuration(MENU_FADE_DURATION);
diff --git a/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimates.java b/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimates.java
new file mode 100644
index 0000000..8f41a60
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimates.java
@@ -0,0 +1,8 @@
+package com.android.systemui.power;
+
+public interface EnhancedEstimates {
+
+ boolean isHybridNotificationEnabled();
+
+ Estimate getEstimate();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimatesImpl.java b/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimatesImpl.java
new file mode 100644
index 0000000..d447542
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimatesImpl.java
@@ -0,0 +1,16 @@
+package com.android.systemui.power;
+
+import android.util.Log;
+
+public class EnhancedEstimatesImpl implements EnhancedEstimates {
+
+ @Override
+ public boolean isHybridNotificationEnabled() {
+ return false;
+ }
+
+ @Override
+ public Estimate getEstimate() {
+ return null;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/power/Estimate.java b/packages/SystemUI/src/com/android/systemui/power/Estimate.java
new file mode 100644
index 0000000..12a8f0a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/Estimate.java
@@ -0,0 +1,11 @@
+package com.android.systemui.power;
+
+public class Estimate {
+ public final long estimateMillis;
+ public final boolean isBasedOnUsage;
+
+ public Estimate(long estimateMillis, boolean isBasedOnUsage) {
+ this.estimateMillis = estimateMillis;
+ this.isBasedOnUsage = isBasedOnUsage;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index c29b362..736286f 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -17,40 +17,40 @@
package com.android.systemui.power;
import android.app.Notification;
-import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.content.IntentFilter;
+import android.icu.text.MeasureFormat;
+import android.icu.text.MeasureFormat.FormatWidth;
+import android.icu.util.Measure;
+import android.icu.util.MeasureUnit;
import android.media.AudioAttributes;
-import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
-import android.os.SystemClock;
import android.os.UserHandle;
-import android.provider.Settings;
import android.support.annotation.VisibleForTesting;
+import android.text.format.DateUtils;
import android.util.Slog;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.internal.notification.SystemNotificationChannels;
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.NotificationChannels;
import java.io.PrintWriter;
import java.text.NumberFormat;
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
public class PowerNotificationWarnings implements PowerUI.WarningsUI {
private static final String TAG = PowerUI.TAG + ".Notification";
@@ -96,8 +96,9 @@
private long mScreenOffTime;
private int mShowing;
- private long mBucketDroppedNegativeTimeMs;
+ private long mWarningTriggerTimeMs;
+ private Estimate mEstimate;
private boolean mWarning;
private boolean mPlaySound;
private boolean mInvalidCharger;
@@ -130,14 +131,22 @@
public void update(int batteryLevel, int bucket, long screenOffTime) {
mBatteryLevel = batteryLevel;
if (bucket >= 0) {
- mBucketDroppedNegativeTimeMs = 0;
+ mWarningTriggerTimeMs = 0;
} else if (bucket < mBucket) {
- mBucketDroppedNegativeTimeMs = System.currentTimeMillis();
+ mWarningTriggerTimeMs = System.currentTimeMillis();
}
mBucket = bucket;
mScreenOffTime = screenOffTime;
}
+ @Override
+ public void updateEstimate(Estimate estimate) {
+ mEstimate = estimate;
+ if (estimate.estimateMillis <= PowerUI.THREE_HOURS_IN_MILLIS) {
+ mWarningTriggerTimeMs = System.currentTimeMillis();
+ }
+ }
+
private void updateNotification() {
if (DEBUG) Slog.d(TAG, "updateNotification mWarning=" + mWarning + " mPlaySound="
+ mPlaySound + " mInvalidCharger=" + mInvalidCharger);
@@ -171,25 +180,43 @@
mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_BAD_CHARGER, n, UserHandle.ALL);
}
- private void showWarningNotification() {
- final int textRes = R.string.battery_low_percent_format;
+ protected void showWarningNotification() {
final String percentage = NumberFormat.getPercentInstance().format((double) mBatteryLevel / 100.0);
+ // get standard notification copy
+ String title = mContext.getString(R.string.battery_low_title);
+ String contentText = mContext.getString(R.string.battery_low_percent_format, percentage);
+
+ // override notification copy if hybrid notification enabled
+ if (mEstimate != null) {
+ title = mContext.getString(R.string.battery_low_title_hybrid);
+ contentText = mContext.getString(
+ mEstimate.isBasedOnUsage
+ ? R.string.battery_low_percent_format_hybrid
+ : R.string.battery_low_percent_format_hybrid_short,
+ percentage,
+ getTimeRemainingFormatted());
+ }
+
final Notification.Builder nb =
new Notification.Builder(mContext, NotificationChannels.BATTERY)
.setSmallIcon(R.drawable.ic_power_low)
// Bump the notification when the bucket dropped.
- .setWhen(mBucketDroppedNegativeTimeMs)
+ .setWhen(mWarningTriggerTimeMs)
.setShowWhen(false)
- .setContentTitle(mContext.getString(R.string.battery_low_title))
- .setContentText(mContext.getString(textRes, percentage))
+ .setContentTitle(title)
+ .setContentText(contentText)
.setOnlyAlertOnce(true)
.setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_WARNING))
- .setVisibility(Notification.VISIBILITY_PUBLIC)
- .setColor(Utils.getColorAttr(mContext, android.R.attr.colorError));
+ .setVisibility(Notification.VISIBILITY_PUBLIC);
if (hasBatterySettings()) {
nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SETTINGS));
}
+ // Make the notification red if the percentage goes below a certain amount or the time
+ // remaining estimate is disabled
+ if (mEstimate == null || mBucket < 0) {
+ nb.setColor(Utils.getColorAttr(mContext, android.R.attr.colorError));
+ }
nb.addAction(0,
mContext.getString(R.string.battery_saver_start_action),
pendingBroadcast(ACTION_START_SAVER));
@@ -201,6 +228,23 @@
mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, n, UserHandle.ALL);
}
+ @VisibleForTesting
+ String getTimeRemainingFormatted() {
+ final Locale currentLocale = mContext.getResources().getConfiguration().getLocales().get(0);
+ MeasureFormat frmt = MeasureFormat.getInstance(currentLocale, FormatWidth.NARROW);
+
+ final long remainder = mEstimate.estimateMillis % DateUtils.HOUR_IN_MILLIS;
+ final long hours = TimeUnit.MILLISECONDS.toHours(
+ mEstimate.estimateMillis - remainder);
+ // round down to the nearest 15 min for now to not appear overly precise
+ final long minutes = TimeUnit.MILLISECONDS.toMinutes(
+ remainder - (remainder % TimeUnit.MINUTES.toMillis(15)));
+ final Measure hoursMeasure = new Measure(hours, MeasureUnit.HOUR);
+ final Measure minutesMeasure = new Measure(minutes, MeasureUnit.MINUTE);
+
+ return frmt.formatMeasures(hoursMeasure, minutesMeasure);
+ }
+
private PendingIntent pendingBroadcast(String action) {
return PendingIntent.getBroadcastAsUser(mContext,
0, new Intent(action), 0, UserHandle.CURRENT);
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index c1a3623..c5aab60 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -52,6 +52,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
public class PowerUI extends SystemUI {
static final String TAG = "PowerUI";
@@ -59,6 +60,7 @@
private static final long TEMPERATURE_INTERVAL = 30 * DateUtils.SECOND_IN_MILLIS;
private static final long TEMPERATURE_LOGGING_INTERVAL = DateUtils.HOUR_IN_MILLIS;
private static final int MAX_RECENT_TEMPS = 125; // TEMPERATURE_LOGGING_INTERVAL plus a buffer
+ static final long THREE_HOURS_IN_MILLIS = DateUtils.HOUR_IN_MILLIS * 3;
private final Handler mHandler = new Handler();
private final Receiver mReceiver = new Receiver();
@@ -68,9 +70,11 @@
private WarningsUI mWarnings;
private final Configuration mLastConfiguration = new Configuration();
private int mBatteryLevel = 100;
+ private long mTimeRemaining = Long.MAX_VALUE;
private int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
private int mPlugType = 0;
private int mInvalidCharger = 0;
+ private EnhancedEstimates mEnhancedEstimates;
private int mLowBatteryAlertCloseLevel;
private final int[] mLowBatteryReminderLevels = new int[2];
@@ -83,8 +87,8 @@
private long mNextLogTime;
private IThermalService mThermalService;
- // We create a method reference here so that we are guaranteed that we can remove a callback
// by using the same instance (method references are not guaranteed to be the same object
+ // We create a method reference here so that we are guaranteed that we can remove a callback
// each time they are created).
private final Runnable mUpdateTempCallback = this::updateTemperatureWarning;
@@ -94,6 +98,7 @@
mContext.getSystemService(Context.HARDWARE_PROPERTIES_SERVICE);
mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
mWarnings = Dependency.get(WarningsUI.class);
+ mEnhancedEstimates = Dependency.get(EnhancedEstimates.class);
mLastConfiguration.setTo(mContext.getResources().getConfiguration());
ContentObserver obs = new ContentObserver(mHandler) {
@@ -131,10 +136,15 @@
com.android.internal.R.integer.config_criticalBatteryWarningLevel);
final ContentResolver resolver = mContext.getContentResolver();
- int defWarnLevel = mContext.getResources().getInteger(
+ final int defWarnLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel);
- int warnLevel = Settings.Global.getInt(resolver,
+ final int lowPowerModeTriggerLevel = Settings.Global.getInt(resolver,
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);
+
+ // NOTE: Keep the logic in sync with BatteryService.
+ // TODO: Propagate this value from BatteryService to system UI, really.
+ int warnLevel = Math.min(defWarnLevel, lowPowerModeTriggerLevel);
+
if (warnLevel == 0) {
warnLevel = defWarnLevel;
}
@@ -231,21 +241,9 @@
return;
}
- boolean isPowerSaver = mPowerManager.isPowerSaveMode();
- if (!plugged
- && !isPowerSaver
- && (bucket < oldBucket || oldPlugged)
- && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
- && bucket < 0) {
+ // Show the correct version of low battery warning if needed
+ maybeShowBatteryWarning(plugged, oldPlugged, oldBucket, bucket);
- // only play SFX when the dialog comes up or the bucket changes
- final boolean playSound = bucket != oldBucket || oldPlugged;
- mWarnings.showLowBatteryWarning(playSound);
- } else if (isPowerSaver || plugged || (bucket > oldBucket && bucket > 0)) {
- mWarnings.dismissLowBatteryWarning();
- } else {
- mWarnings.updateLowBatteryWarning();
- }
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
mScreenOffTime = SystemClock.elapsedRealtime();
} else if (Intent.ACTION_SCREEN_ON.equals(action)) {
@@ -256,7 +254,65 @@
Slog.w(TAG, "unknown intent: " + intent);
}
}
- };
+ }
+
+ protected void maybeShowBatteryWarning(boolean plugged, boolean oldPlugged, int oldBucket,
+ int bucket) {
+ boolean isPowerSaver = mPowerManager.isPowerSaveMode();
+ // only play SFX when the dialog comes up or the bucket changes
+ final boolean playSound = bucket != oldBucket || oldPlugged;
+ long oldTimeRemaining = mTimeRemaining;
+ if (mEnhancedEstimates.isHybridNotificationEnabled()) {
+ final Estimate estimate = mEnhancedEstimates.getEstimate();
+ // Turbo is not always booted once SysUI is running so we have ot make sure we actually
+ // get data back
+ if (estimate != null) {
+ mTimeRemaining = estimate.estimateMillis;
+ mWarnings.updateEstimate(estimate);
+ }
+ }
+
+ if (shouldShowLowBatteryWarning(plugged, oldPlugged, oldBucket, bucket, oldTimeRemaining,
+ mTimeRemaining,
+ isPowerSaver, mBatteryStatus)) {
+ mWarnings.showLowBatteryWarning(playSound);
+ } else if (shouldDismissLowBatteryWarning(plugged, oldBucket, bucket, mTimeRemaining,
+ isPowerSaver)) {
+ mWarnings.dismissLowBatteryWarning();
+ } else {
+ mWarnings.updateLowBatteryWarning();
+ }
+ }
+
+ @VisibleForTesting
+ boolean shouldShowLowBatteryWarning(boolean plugged, boolean oldPlugged, int oldBucket,
+ int bucket, long oldTimeRemaining, long timeRemaining,
+ boolean isPowerSaver, int mBatteryStatus) {
+ return !plugged
+ && !isPowerSaver
+ && (((bucket < oldBucket || oldPlugged) && bucket < 0)
+ || (mEnhancedEstimates.isHybridNotificationEnabled()
+ && timeRemaining < THREE_HOURS_IN_MILLIS
+ && isHourLess(oldTimeRemaining, timeRemaining)))
+ && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN;
+ }
+
+ private boolean isHourLess(long oldTimeRemaining, long timeRemaining) {
+ final long dif = oldTimeRemaining - timeRemaining;
+ return dif >= TimeUnit.HOURS.toMillis(1);
+ }
+
+ @VisibleForTesting
+ boolean shouldDismissLowBatteryWarning(boolean plugged, int oldBucket, int bucket,
+ long timeRemaining, boolean isPowerSaver) {
+ final boolean hybridWouldDismiss = mEnhancedEstimates.isHybridNotificationEnabled()
+ && timeRemaining > THREE_HOURS_IN_MILLIS;
+ final boolean standardWouldDismiss = (bucket > oldBucket && bucket > 0);
+ return isPowerSaver
+ || plugged
+ || (standardWouldDismiss && (!mEnhancedEstimates.isHybridNotificationEnabled()
+ || hybridWouldDismiss));
+ }
private void initTemperatureWarning() {
ContentResolver resolver = mContext.getContentResolver();
@@ -428,6 +484,7 @@
public interface WarningsUI {
void update(int batteryLevel, int bucket, long screenOffTime);
+ void updateEstimate(Estimate estimate);
void dismissLowBatteryWarning();
void showLowBatteryWarning(boolean playSound);
void dismissInvalidChargerWarning();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 33b5268..7f0acc2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -17,13 +17,18 @@
package com.android.systemui.qs;
import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Path;
import android.graphics.Point;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
+import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.statusbar.ExpandableOutlineView;
/**
* Wrapper view with background which contains {@link QSPanel} and {@link BaseStatusBarHeader}
@@ -31,6 +36,7 @@
public class QSContainerImpl extends FrameLayout {
private final Point mSizePoint = new Point();
+ private final Path mClipPath = new Path();
private int mHeightOverride = -1;
protected View mQSPanel;
@@ -40,6 +46,7 @@
private QSCustomizer mQSCustomizer;
private View mQSFooter;
private float mFullElevation;
+ private float mRadius;
public QSContainerImpl(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -54,6 +61,8 @@
mQSCustomizer = findViewById(R.id.qs_customize);
mQSFooter = findViewById(R.id.qs_footer);
mFullElevation = mQSPanel.getElevation();
+ mRadius = getResources().getDimensionPixelSize(
+ Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
setClickable(true);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
@@ -93,6 +102,18 @@
updateExpansion();
}
+ @Override
+ protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+ boolean ret;
+ canvas.save();
+ if (child != mQSCustomizer) {
+ canvas.clipPath(mClipPath);
+ }
+ ret = super.drawChild(canvas, child, drawingTime);
+ canvas.restore();
+ return ret;
+ }
+
/**
* Overrides the height of this view (post-layout), so that the content is clipped to that
* height and the background is set to that height.
@@ -110,6 +131,10 @@
mQSDetail.setBottom(getTop() + height);
// Pin QS Footer to the bottom of the panel.
mQSFooter.setTranslationY(height - mQSFooter.getHeight());
+
+ ExpandableOutlineView.getRoundedRectPath(0, 0, getWidth(), height, mRadius,
+ mRadius,
+ mClipPath);
}
protected int calculateContainerHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index c249e37..0f83078 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -103,7 +103,7 @@
if (iv instanceof SlashImageView) {
((SlashImageView) iv).setAnimationEnabled(shouldAnimate);
- ((SlashImageView) iv).setState(state.slash, d);
+ ((SlashImageView) iv).setState(null, d);
} else {
iv.setImageDrawable(d);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 4d0e60d..acd327b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -13,7 +13,12 @@
*/
package com.android.systemui.qs.tileimpl;
+import static com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
@@ -22,16 +27,21 @@
import android.os.Message;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
+import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
import android.widget.Switch;
+import com.android.settingslib.Utils;
import com.android.systemui.R;
-import com.android.systemui.plugins.qs.*;
+import com.android.systemui.plugins.qs.QSIconView;
+import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
@@ -47,6 +57,12 @@
private boolean mCollapsedView;
private boolean mClicked;
+ private final ImageView mBg;
+ private final int mColorActive;
+ private final int mColorInactive;
+ private final int mColorDisabled;
+ private int mCircleColor;
+
public QSTileBaseView(Context context, QSIconView icon) {
this(context, icon, false);
}
@@ -60,6 +76,10 @@
mIconFrame.setForegroundGravity(Gravity.CENTER);
int size = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
addView(mIconFrame, new LayoutParams(size, size));
+ mBg = new ImageView(getContext());
+ mBg.setScaleType(ScaleType.FIT_CENTER);
+ mBg.setImageResource(R.drawable.ic_qs_circle);
+ mIconFrame.addView(mBg);
mIcon = icon;
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
@@ -73,6 +93,11 @@
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
setBackground(mTileBackground);
+ mColorActive = Utils.getColorAttr(context, android.R.attr.colorAccent);
+ mColorDisabled = Utils.getDisabled(context,
+ Utils.getColorAttr(context, android.R.attr.textColorTertiary));
+ mColorInactive = Utils.getColorAttr(context, android.R.attr.textColorSecondary);
+
setPadding(0, 0, 0, 0);
setClipChildren(false);
setClipToPadding(false);
@@ -80,6 +105,10 @@
setFocusable(true);
}
+ public View getBgCicle() {
+ return mBg;
+ }
+
protected Drawable newTileBackground() {
final int[] attrs = new int[]{android.R.attr.selectableItemBackgroundBorderless};
final TypedArray ta = getContext().obtainStyledAttributes(attrs);
@@ -150,6 +179,20 @@
}
protected void handleStateChanged(QSTile.State state) {
+ int circleColor = getCircleColor(state.state);
+ if (circleColor != mCircleColor) {
+ if (mBg.isShown()) {
+ ValueAnimator animator = ValueAnimator.ofArgb(mCircleColor, circleColor)
+ .setDuration(QS_ANIM_LENGTH);
+ animator.addUpdateListener(animation -> mBg.setImageTintList(ColorStateList.valueOf(
+ (Integer) animation.getAnimatedValue())));
+ animator.start();
+ } else {
+ QSIconViewImpl.setTint(mBg, circleColor);
+ }
+ mCircleColor = circleColor;
+ }
+
setClickable(state.state != Tile.STATE_UNAVAILABLE);
mIcon.setIcon(state);
setContentDescription(state.contentDescription);
@@ -163,6 +206,19 @@
}
}
+ private int getCircleColor(int state) {
+ switch (state) {
+ case Tile.STATE_ACTIVE:
+ return mColorActive;
+ case Tile.STATE_INACTIVE:
+ case Tile.STATE_UNAVAILABLE:
+ return mColorDisabled;
+ default:
+ Log.e(TAG, "Invalid state " + state);
+ return 0;
+ }
+ }
+
@Override
public void setClickable(boolean clickable) {
super.setClickable(clickable);
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 576a447..72592829 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -373,11 +373,11 @@
switch (state) {
case Tile.STATE_UNAVAILABLE:
return Utils.getDisabled(context,
- Utils.getColorAttr(context, android.R.attr.colorForeground));
+ Utils.getColorAttr(context, android.R.attr.textColorSecondary));
case Tile.STATE_INACTIVE:
- return Utils.getColorAttr(context, android.R.attr.textColorHint);
+ return Utils.getColorAttr(context, android.R.attr.textColorSecondary);
case Tile.STATE_ACTIVE:
- return Utils.getColorAttr(context, android.R.attr.textColorPrimary);
+ return Utils.getColorAttr(context, android.R.attr.colorPrimary);
default:
Log.e("QSTile", "Invalid state " + state);
return 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index 263dac0..a226f3c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -18,6 +18,7 @@
import android.content.res.Configuration;
import android.service.quicksettings.Tile;
import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -28,6 +29,7 @@
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
+import com.android.systemui.R.id;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
@@ -36,8 +38,10 @@
/** View that represents a standard quick settings tile. **/
public class QSTileView extends QSTileBaseView {
+ private static final boolean DUAL_TARGET_ALLOWED = false;
private View mDivider;
protected TextView mLabel;
+ private TextView mSecondLine;
private ImageView mPadLock;
private int mState;
private ViewGroup mLabelContainer;
@@ -86,6 +90,8 @@
mDivider = mLabelContainer.findViewById(R.id.underline);
mExpandIndicator = mLabelContainer.findViewById(R.id.expand_indicator);
mExpandSpace = mLabelContainer.findViewById(R.id.expand_space);
+ mSecondLine = mLabelContainer.findViewById(R.id.app_label);
+ mSecondLine.setAlpha(.6f);
addView(mLabelContainer);
}
@@ -103,14 +109,20 @@
mState = state.state;
mLabel.setText(state.label);
}
- mExpandIndicator.setVisibility(state.dualTarget ? View.VISIBLE : View.GONE);
- mExpandSpace.setVisibility(state.dualTarget ? View.VISIBLE : View.GONE);
- mLabelContainer.setContentDescription(state.dualTarget ? state.dualLabelContentDescription
+ if (!Objects.equal(mSecondLine.getText(), state.secondaryLabel)) {
+ mSecondLine.setText(state.secondaryLabel);
+ mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel) ? View.GONE
+ : View.VISIBLE);
+ }
+ boolean dualTarget = DUAL_TARGET_ALLOWED && state.dualTarget;
+ mExpandIndicator.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
+ mExpandSpace.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
+ mLabelContainer.setContentDescription(dualTarget ? state.dualLabelContentDescription
: null);
- if (state.dualTarget != mLabelContainer.isClickable()) {
- mLabelContainer.setClickable(state.dualTarget);
- mLabelContainer.setLongClickable(state.dualTarget);
- mLabelContainer.setBackground(state.dualTarget ? newTileBackground() : null);
+ if (dualTarget != mLabelContainer.isClickable()) {
+ mLabelContainer.setClickable(dualTarget);
+ mLabelContainer.setLongClickable(dualTarget);
+ mLabelContainer.setBackground(dualTarget ? newTileBackground() : null);
}
mLabel.setEnabled(!state.disabledByPolicy);
mPadLock.setVisibility(state.disabledByPolicy ? View.VISIBLE : View.GONE);
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 0e4a9fe..fff9f8e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -125,11 +125,11 @@
state.slash = new SlashState();
}
state.slash.isSlashed = !enabled;
+ state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
if (enabled) {
- state.label = null;
if (connected) {
state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_connected);
- state.label = mController.getLastDeviceName();
+ state.secondaryLabel = mController.getLastDeviceName();
CachedBluetoothDevice lastDevice = mController.getLastDevice();
if (lastDevice != null) {
int batteryLevel = lastDevice.getBatteryLevel();
@@ -140,25 +140,20 @@
}
}
state.contentDescription = mContext.getString(
- R.string.accessibility_bluetooth_name, state.label);
+ R.string.accessibility_bluetooth_name, state.secondaryLabel);
} else if (state.isTransient) {
state.icon = ResourceIcon.get(R.drawable.ic_bluetooth_transient_animation);
state.contentDescription = mContext.getString(
R.string.accessibility_quick_settings_bluetooth_connecting);
- state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
} else {
state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_on);
state.contentDescription = mContext.getString(
R.string.accessibility_quick_settings_bluetooth_on) + ","
+ mContext.getString(R.string.accessibility_not_connected);
}
- if (TextUtils.isEmpty(state.label)) {
- state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
- }
state.state = Tile.STATE_ACTIVE;
} else {
state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_on);
- state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
state.contentDescription = mContext.getString(
R.string.accessibility_quick_settings_bluetooth_off);
state.state = Tile.STATE_INACTIVE;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index f3bae20..34b8bfe 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -67,6 +67,8 @@
case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
mScreenshot.takeScreenshotPartial(finisher, msg.arg1 > 0, msg.arg2 > 0);
break;
+ default:
+ Log.d(TAG, "Invalid screenshot option: " + msg.what);
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index ff0357a..e59c703 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -931,13 +931,6 @@
}
@Override
- public void setCurrentSidePaddings(float currentSidePaddings) {
- super.setCurrentSidePaddings(currentSidePaddings);
- mBackgroundNormal.setCurrentSidePaddings(currentSidePaddings);
- mBackgroundDimmed.setCurrentSidePaddings(currentSidePaddings);
- }
-
- @Override
protected boolean childNeedsClipping(View child) {
if (child instanceof NotificationBackgroundView && isClippingNeeded()) {
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index f53eb48..5f4854a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -33,6 +33,7 @@
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
+import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
import android.util.FloatProperty;
@@ -173,6 +174,7 @@
private FalsingManager mFalsingManager;
private AboveShelfChangedListener mAboveShelfChangedListener;
private HeadsUpManager mHeadsUpManager;
+ private View mHelperButton;
private boolean mJustClicked;
private boolean mIconAnimationRunning;
@@ -387,6 +389,9 @@
updateLimits();
updateIconVisibilities();
updateShelfIconColor();
+
+ showBlockingHelper(mEntry.userSentiment ==
+ NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
}
@VisibleForTesting
@@ -1318,6 +1323,10 @@
requestLayout();
}
+ public void showBlockingHelper(boolean show) {
+ mHelperButton.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
@@ -1325,6 +1334,12 @@
mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
mLayouts = new NotificationContentView[] {mPrivateLayout, mPublicLayout};
+ final NotificationGutsManager gutsMan = Dependency.get(NotificationGutsManager.class);
+ mHelperButton = findViewById(R.id.helper);
+ mHelperButton.setOnClickListener(view -> {
+ doLongClickCallback();
+ });
+
for (NotificationContentView l : mLayouts) {
l.setExpandClickListener(mExpandClickListener);
l.setContainingNotification(this);
@@ -2362,16 +2377,17 @@
NotificationContentView contentView = (NotificationContentView) child;
if (isClippingNeeded()) {
return true;
- } else if (!hasNoRoundingAndNoPadding() && contentView.shouldClipToSidePaddings()) {
+ } else if (!hasNoRounding()
+ && contentView.shouldClipToRounding(getCurrentTopRoundness() != 0.0f,
+ getCurrentBottomRoundness() != 0.0f)) {
return true;
}
} else if (child == mChildrenContainer) {
- if (isClippingNeeded() || ((isGroupExpanded() || isGroupExpansionChanging())
- && getClipBottomAmount() != 0.0f && getCurrentBottomRoundness() != 0.0f)) {
+ if (isClippingNeeded() || !hasNoRounding()) {
return true;
}
} else if (child instanceof NotificationGuts) {
- return !hasNoRoundingAndNoPadding();
+ return !hasNoRounding();
}
return super.childNeedsClipping(child);
}
@@ -2401,9 +2417,8 @@
return super.getCustomClipPath(child);
}
- private boolean hasNoRoundingAndNoPadding() {
- return mCurrentSidePaddings == 0 && getCurrentBottomRoundness() == 0.0f
- && getCurrentTopRoundness() == 0.0f;
+ private boolean hasNoRounding() {
+ return getCurrentBottomRoundness() == 0.0f && getCurrentTopRoundness() == 0.0f;
}
public boolean isShowingAmbient() {
@@ -2418,20 +2433,6 @@
}
}
- @Override
- public void setCurrentSidePaddings(float currentSidePaddings) {
- if (mIsSummaryWithChildren) {
- List<ExpandableNotificationRow> notificationChildren =
- mChildrenContainer.getNotificationChildren();
- int size = notificationChildren.size();
- for (int i = 0; i < size; i++) {
- ExpandableNotificationRow row = notificationChildren.get(i);
- row.setCurrentSidePaddings(currentSidePaddings);
- }
- }
- super.setCurrentSidePaddings(currentSidePaddings);
- }
-
public static class NotificationViewState extends ExpandableViewState {
private final StackScrollState mOverallState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
index db19d2f..66b3a75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -58,6 +58,7 @@
private static final Path EMPTY_PATH = new Path();
private final Rect mOutlineRect = new Rect();
+ private final Path mClipPath = new Path();
private boolean mCustomOutline;
private float mOutlineAlpha = -1f;
private float mOutlineRadius;
@@ -69,13 +70,14 @@
private float mBottomRoundness;
private float mTopRoundness;
private int mBackgroundTop;
- protected int mCurrentSidePaddings;
/**
* {@code true} if the children views of the {@link ExpandableOutlineView} are translated when
* it is moved. Otherwise, the translation is set on the {@code ExpandableOutlineView} itself.
*/
protected boolean mShouldTranslateContents;
+ private boolean mClipRoundedToClipTopAmount;
+ private float mDistanceToTopRoundness = -1;
private final ViewOutlineProvider mProvider = new ViewOutlineProvider() {
@Override
@@ -83,9 +85,9 @@
if (!mCustomOutline && mCurrentTopRoundness == 0.0f
&& mCurrentBottomRoundness == 0.0f && !mAlwaysRoundBothCorners) {
int translation = mShouldTranslateContents ? (int) getTranslation() : 0;
- int left = Math.max(translation + mCurrentSidePaddings, mCurrentSidePaddings);
+ int left = Math.max(translation, 0);
int top = mClipTopAmount + mBackgroundTop;
- int right = getWidth() - mCurrentSidePaddings + Math.min(translation, 0);
+ int right = getWidth() + Math.min(translation, 0);
int bottom = Math.max(getActualHeight() - mClipBottomAmount, top);
outline.setRect(left, top, right, bottom);
} else {
@@ -115,9 +117,9 @@
if (!mCustomOutline) {
int translation = mShouldTranslateContents && !ignoreTranslation
? (int) getTranslation() : 0;
- left = Math.max(translation + mCurrentSidePaddings, mCurrentSidePaddings);
+ left = Math.max(translation, 0);
top = mClipTopAmount + mBackgroundTop;
- right = getWidth() - mCurrentSidePaddings + Math.min(translation, 0);
+ right = getWidth() + Math.min(translation, 0);
bottom = Math.max(getActualHeight(), top);
int intersectBottom = Math.max(getActualHeight() - mClipBottomAmount, top);
if (bottom != intersectBottom) {
@@ -135,8 +137,6 @@
top = mOutlineRect.top;
right = mOutlineRect.right;
bottom = mOutlineRect.bottom;
- left = Math.max(mCurrentSidePaddings, left);
- right = Math.min(getWidth() - mCurrentSidePaddings, right);
}
height = bottom - top;
if (height == 0) {
@@ -162,15 +162,8 @@
return roundedRectPath;
}
- protected Path getRoundedRectPath(int left, int top, int right, int bottom, float topRoundness,
- float bottomRoundness) {
- getRoundedRectPath(left, top, right, bottom, topRoundness, bottomRoundness,
- mTmpPath);
- return mTmpPath;
- }
-
- private void getRoundedRectPath(int left, int top, int right, int bottom, float topRoundness,
- float bottomRoundness, Path outPath) {
+ public static void getRoundedRectPath(int left, int top, int right, int bottom,
+ float topRoundness, float bottomRoundness, Path outPath) {
outPath.reset();
int width = right - left;
float topRoundnessX = topRoundness;
@@ -207,20 +200,50 @@
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
canvas.save();
+ Path intersectPath = null;
+ if (mClipRoundedToClipTopAmount) {
+ int left = 0;
+ int top = (int) (mClipTopAmount - mDistanceToTopRoundness);
+ int right = getWidth();
+ int bottom = (int) Math.max(getActualHeight() - mClipBottomAmount,
+ top + mOutlineRadius);
+ ExpandableOutlineView.getRoundedRectPath(left, top, right, bottom, mOutlineRadius,
+ 0.0f,
+ mClipPath);
+ intersectPath = mClipPath;
+ }
+ boolean clipped = false;
if (childNeedsClipping(child)) {
Path clipPath = getCustomClipPath(child);
if (clipPath == null) {
clipPath = getClipPath();
}
if (clipPath != null) {
+ if (intersectPath != null) {
+ clipPath.op(intersectPath, Path.Op.INTERSECT);
+ }
canvas.clipPath(clipPath);
+ clipped = true;
}
}
+ if (!clipped && intersectPath != null) {
+ canvas.clipPath(intersectPath);
+ }
boolean result = super.drawChild(canvas, child, drawingTime);
canvas.restore();
return result;
}
+ @Override
+ public void setDistanceToTopRoundness(float distanceToTopRoundness) {
+ super.setDistanceToTopRoundness(distanceToTopRoundness);
+ if (distanceToTopRoundness != mDistanceToTopRoundness) {
+ mClipRoundedToClipTopAmount = distanceToTopRoundness >= 0;
+ mDistanceToTopRoundness = distanceToTopRoundness;
+ invalidate();
+ }
+ }
+
protected boolean childNeedsClipping(View child) {
return false;
}
@@ -395,10 +418,4 @@
public Path getCustomClipPath(View child) {
return null;
}
-
- public void setCurrentSidePaddings(float currentSidePaddings) {
- mCurrentSidePaddings = (int) currentSidePaddings;
- invalidateOutline();
- invalidate();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index f762513..eafa825 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -130,6 +130,14 @@
}
}
+ /**
+ * Set the distance to the top roundness, from where we should start clipping a value above
+ * or equal to 0 is the effective distance, and if a value below 0 is received, there should
+ * be no clipping.
+ */
+ public void setDistanceToTopRoundness(float distanceToTopRoundness) {
+ }
+
public void setActualHeight(int actualHeight) {
setActualHeight(actualHeight, true /* notifyListeners */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 569e58d..43047ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -52,6 +52,8 @@
import com.android.systemui.util.wakelock.SettableWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
+import java.text.NumberFormat;
+
/**
* Controls the indications and error messages shown on the Keyguard
*/
@@ -78,7 +80,7 @@
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private String mRestingIndication;
- private String mTransientIndication;
+ private CharSequence mTransientIndication;
private int mTransientTextColor;
private int mInitialTextColor;
private boolean mVisible;
@@ -87,6 +89,7 @@
private boolean mPowerCharged;
private int mChargingSpeed;
private int mChargingWattage;
+ private int mBatteryLevel;
private String mMessageToShowOnScreenOn;
private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
@@ -246,14 +249,14 @@
/**
* Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
*/
- public void showTransientIndication(String transientIndication) {
+ public void showTransientIndication(CharSequence transientIndication) {
showTransientIndication(transientIndication, mInitialTextColor);
}
/**
* Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
*/
- public void showTransientIndication(String transientIndication, int textColor) {
+ public void showTransientIndication(CharSequence transientIndication, int textColor) {
mTransientIndication = transientIndication;
mTransientTextColor = textColor;
mHandler.removeMessages(MSG_HIDE_TRANSIENT);
@@ -285,14 +288,18 @@
// Walk down a precedence-ordered list of what indication
// should be shown based on user or device state
if (mDozing) {
- // If we're dozing, never show a persistent indication.
+ mTextView.setTextColor(Color.WHITE);
if (!TextUtils.isEmpty(mTransientIndication)) {
// When dozing we ignore any text color and use white instead, because
// colors can be hard to read in low brightness.
- mTextView.setTextColor(Color.WHITE);
mTextView.switchIndication(mTransientIndication);
+ } else if (mPowerPluggedIn) {
+ String indication = computePowerIndication();
+ mTextView.switchIndication(indication);
} else {
- mTextView.switchIndication(null);
+ String percentage = NumberFormat.getPercentInstance()
+ .format(mBatteryLevel / 100f);
+ mTextView.switchIndication(percentage);
}
return;
}
@@ -422,6 +429,7 @@
mPowerCharged = status.isCharged();
mChargingWattage = status.maxChargingWattage;
mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
+ mBatteryLevel = status.level;
updateIndication();
if (mDozing) {
if (!wasPluggedIn && mPowerPluggedIn) {
@@ -490,6 +498,12 @@
}
@Override
+ public void onTrustAgentErrorMessage(CharSequence message) {
+ int errorColor = Utils.getColorError(mContext);
+ showTransientIndication(message, errorColor);
+ }
+
+ @Override
public void onScreenTurnedOn() {
if (mMessageToShowOnScreenOn != null) {
int errorColor = Utils.getColorError(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
index 68cf51c..45b35d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
@@ -41,7 +41,6 @@
private int mClipBottomAmount;
private int mTintColor;
private float[] mCornerRadii = new float[8];
- private int mCurrentSidePaddings;
private boolean mBottomIsRounded;
private int mBackgroundTop;
private boolean mBottomAmountClips = true;
@@ -68,8 +67,7 @@
if (mBottomIsRounded && mBottomAmountClips) {
bottom -= mClipBottomAmount;
}
- drawable.setBounds(mCurrentSidePaddings, mBackgroundTop,
- getWidth() - mCurrentSidePaddings, bottom);
+ drawable.setBounds(0, mBackgroundTop, getWidth(), bottom);
drawable.draw(canvas);
}
}
@@ -206,11 +204,6 @@
}
}
- public void setCurrentSidePaddings(float currentSidePaddings) {
- mCurrentSidePaddings = (int) currentSidePaddings;
- invalidate();
- }
-
public void setBackgroundTop(int backgroundTop) {
mBackgroundTop = backgroundTop;
invalidate();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 39c2131..64df92c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -136,7 +136,6 @@
private int mClipBottomAmount;
private boolean mIsLowPriority;
private boolean mIsContentExpandable;
- private int mCustomViewSidePaddings;
public NotificationContentView(Context context, AttributeSet attrs) {
@@ -150,8 +149,6 @@
R.dimen.min_notification_layout_height);
mNotificationContentMarginEnd = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.notification_content_margin_end);
- mCustomViewSidePaddings = getResources().getDimensionPixelSize(
- R.dimen.notification_content_custom_view_side_padding);
}
public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight,
@@ -391,22 +388,6 @@
mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child,
mContainingNotification);
mContractedWrapper.setDark(mDark, false /* animate */, 0 /* delay */);
- updateMargins(child);
- }
-
- private void updateMargins(View child) {
- if (child == null) {
- return;
- }
- NotificationViewWrapper wrapper = getWrapperForView(child);
- boolean isCustomView = wrapper instanceof NotificationCustomViewWrapper;
- boolean needsMargins = isCustomView &&
- child.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P;
- int padding = needsMargins ? mCustomViewSidePaddings : 0;
- MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams();
- layoutParams.setMarginStart(padding);
- layoutParams.setMarginEnd(padding);
- child.setLayoutParams(layoutParams);
}
private NotificationViewWrapper getWrapperForView(View child) {
@@ -456,7 +437,6 @@
mExpandedChild = child;
mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child,
mContainingNotification);
- updateMargins(child);
}
public void setHeadsUpChild(View child) {
@@ -490,7 +470,6 @@
mHeadsUpChild = child;
mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child,
mContainingNotification);
- updateMargins(child);
}
public void setAmbientChild(View child) {
@@ -1510,19 +1489,21 @@
return false;
}
- public boolean shouldClipToSidePaddings() {
- boolean needsPaddings = shouldClipToSidePaddings(getVisibleType());
+ public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
+ boolean needsPaddings = shouldClipToRounding(getVisibleType(), topRounded, bottomRounded);
if (mUserExpanding) {
- needsPaddings |= shouldClipToSidePaddings(mTransformationStartVisibleType);
+ needsPaddings |= shouldClipToRounding(mTransformationStartVisibleType, topRounded,
+ bottomRounded);
}
return needsPaddings;
}
- private boolean shouldClipToSidePaddings(int visibleType) {
+ private boolean shouldClipToRounding(int visibleType, boolean topRounded,
+ boolean bottomRounded) {
NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
if (visibleWrapper == null) {
return false;
}
- return visibleWrapper.shouldClipToSidePaddings();
+ return visibleWrapper.shouldClipToRounding(topRounded, bottomRounded);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index d0417b5..127f3f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -67,6 +67,7 @@
public static final class Entry {
private static final long LAUNCH_COOLDOWN = 2000;
+ private static final long REMOTE_INPUT_COOLDOWN = 500;
private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN;
private static final int COLOR_INVALID = 1;
public String key;
@@ -86,10 +87,14 @@
public RemoteViews cachedAmbientContentView;
public CharSequence remoteInputText;
public List<SnoozeCriterion> snoozeCriteria;
+ public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL;
+
private int mCachedContrastColor = COLOR_INVALID;
private int mCachedContrastColorIsFor = COLOR_INVALID;
private InflationTask mRunningTask = null;
private Throwable mDebugThrowable;
+ public CharSequence remoteInputTextWhenReset;
+ public long lastRemoteInputSent = NOT_LAUNCHED_YET;
public Entry(StatusBarNotification n) {
this.key = n.getKey();
@@ -130,6 +135,10 @@
return SystemClock.elapsedRealtime() < lastFullScreenIntentLaunchTime + LAUNCH_COOLDOWN;
}
+ public boolean hasJustSentRemoteInput() {
+ return SystemClock.elapsedRealtime() < lastRemoteInputSent + REMOTE_INPUT_COOLDOWN;
+ }
+
/**
* Create the icons for a notification
* @param context the context to create the icons with
@@ -263,6 +272,11 @@
public Throwable getDebugThrowable() {
return mDebugThrowable;
}
+
+ public void onRemoteInputInserted() {
+ lastRemoteInputSent = NOT_LAUNCHED_YET;
+ remoteInputTextWhenReset = null;
+ }
}
private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
@@ -463,6 +477,7 @@
}
entry.channel = getChannel(entry.key);
entry.snoozeCriteria = getSnoozeCriteria(entry.key);
+ entry.userSentiment = mTmpRanking.getUserSentiment();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
index 6bbd09f..5b06874 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
@@ -35,6 +35,7 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.Log;
@@ -462,7 +463,8 @@
mMediaManager.onNotificationRemoved(key);
NotificationData.Entry entry = mNotificationData.get(key);
- if (FORCE_REMOTE_INPUT_HISTORY && mRemoteInputManager.getController().isSpinning(key)
+ if (FORCE_REMOTE_INPUT_HISTORY
+ && shouldKeepForRemoteInput(entry)
&& entry.row != null && !entry.row.isDismissed()) {
StatusBarNotification sbn = entry.notification;
@@ -477,7 +479,11 @@
newHistory = new CharSequence[oldHistory.length + 1];
System.arraycopy(oldHistory, 0, newHistory, 1, oldHistory.length);
}
- newHistory[0] = String.valueOf(entry.remoteInputText);
+ CharSequence remoteInputText = entry.remoteInputText;
+ if (TextUtils.isEmpty(remoteInputText)) {
+ remoteInputText = entry.remoteInputTextWhenReset;
+ }
+ newHistory[0] = String.valueOf(remoteInputText);
b.setRemoteInputHistory(newHistory);
Notification newNotification = b.build();
@@ -492,6 +498,7 @@
sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
newNotification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
boolean updated = false;
+ entry.onRemoteInputInserted();
try {
updateNotificationInternal(newSbn, null);
updated = true;
@@ -539,6 +546,19 @@
mCallback.onNotificationRemoved(key, old);
}
+ private boolean shouldKeepForRemoteInput(NotificationData.Entry entry) {
+ if (entry == null) {
+ return false;
+ }
+ if (mRemoteInputManager.getController().isSpinning(entry.key)) {
+ return true;
+ }
+ if (entry.hasJustSentRemoteInput()) {
+ return true;
+ }
+ return false;
+ }
+
private StatusBarNotification removeNotificationViews(String key,
NotificationListenerService.RankingMap ranking) {
NotificationData.Entry entry = mNotificationData.remove(key, ranking);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
index 87ad6f6b..441c184 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
@@ -229,10 +229,9 @@
}
}
try {
- info.bindNotification(pmUser, iNotificationManager, pkg, new ArrayList(channels),
- row.getEntry().channel.getImportance(), sbn, onSettingsClick,
- onAppSettingsClick, onDoneClick, mCheckSaveListener,
- mNonBlockablePkgs);
+ info.bindNotification(pmUser, iNotificationManager, pkg, row.getEntry().channel,
+ channels.size(), sbn, mCheckSaveListener, onSettingsClick,
+ onAppSettingsClick, mNonBlockablePkgs);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index 4301817..1127075 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -128,6 +128,7 @@
mComparators.add(HeaderProcessor.forTextView(mRow,
com.android.internal.R.id.header_text));
mDividers.add(com.android.internal.R.id.header_text_divider);
+ mDividers.add(com.android.internal.R.id.header_text_secondary_divider);
mDividers.add(com.android.internal.R.id.time_divider);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index 8d1bb5f..6279fdc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -18,6 +18,10 @@
import static android.app.NotificationManager.IMPORTANCE_NONE;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -38,12 +42,12 @@
import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageView;
import android.widget.LinearLayout;
-import android.widget.Switch;
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.Utils;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
import java.lang.IllegalArgumentException;
@@ -57,25 +61,37 @@
private static final String TAG = "InfoGuts";
private INotificationManager mINotificationManager;
+ private PackageManager mPm;
+
private String mPkg;
private String mAppName;
private int mAppUid;
- private List<NotificationChannel> mNotificationChannels;
+ private int mNumNotificationChannels;
private NotificationChannel mSingleNotificationChannel;
- private boolean mIsSingleDefaultChannel;
- private StatusBarNotification mSbn;
private int mStartingUserImportance;
+ private int mChosenImportance;
+ private boolean mIsSingleDefaultChannel;
+ private boolean mNonblockable;
+ private StatusBarNotification mSbn;
+ private AnimatorSet mExpandAnimation;
- private TextView mNumChannelsView;
- private View mChannelDisabledView;
- private TextView mSettingsLinkView;
- private Switch mChannelEnabledSwitch;
private CheckSaveListener mCheckSaveListener;
+ private OnSettingsClickListener mOnSettingsClickListener;
private OnAppSettingsClickListener mAppSettingsClickListener;
- private PackageManager mPm;
-
private NotificationGuts mGutsContainer;
+ private OnClickListener mOnKeepShowing = v -> {
+ closeControls(v);
+ };
+
+ private OnClickListener mOnStopNotifications = v -> {
+ swapContent(false);
+ };
+
+ private OnClickListener mOnUndo = v -> {
+ swapContent(true);
+ };
+
public NotificationInfo(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -98,141 +114,93 @@
public void bindNotification(final PackageManager pm,
final INotificationManager iNotificationManager,
final String pkg,
- final List<NotificationChannel> notificationChannels,
- int startingUserImportance,
+ final NotificationChannel notificationChannel,
+ final int numChannels,
final StatusBarNotification sbn,
- OnSettingsClickListener onSettingsClick,
- OnAppSettingsClickListener onAppSettingsClick,
- OnClickListener onDoneClick,
- CheckSaveListener checkSaveListener,
+ final CheckSaveListener checkSaveListener,
+ final OnSettingsClickListener onSettingsClick,
+ final OnAppSettingsClickListener onAppSettingsClick,
final Set<String> nonBlockablePkgs)
throws RemoteException {
mINotificationManager = iNotificationManager;
mPkg = pkg;
- mNotificationChannels = notificationChannels;
- mCheckSaveListener = checkSaveListener;
+ mNumNotificationChannels = numChannels;
mSbn = sbn;
mPm = pm;
mAppSettingsClickListener = onAppSettingsClick;
- mStartingUserImportance = startingUserImportance;
mAppName = mPkg;
- Drawable pkgicon = null;
- CharSequence channelNameText = "";
- ApplicationInfo info = null;
- try {
- info = pm.getApplicationInfo(mPkg,
- PackageManager.MATCH_UNINSTALLED_PACKAGES
- | PackageManager.MATCH_DISABLED_COMPONENTS
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_DIRECT_BOOT_AWARE);
- if (info != null) {
- mAppUid = sbn.getUid();
- mAppName = String.valueOf(pm.getApplicationLabel(info));
- pkgicon = pm.getApplicationIcon(info);
- }
- } catch (PackageManager.NameNotFoundException e) {
- // app is gone, just show package name and generic icon
- pkgicon = pm.getDefaultActivityIcon();
- }
- ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
+ mCheckSaveListener = checkSaveListener;
+ mOnSettingsClickListener = onSettingsClick;
+ mSingleNotificationChannel = notificationChannel;
+ mStartingUserImportance = mChosenImportance = mSingleNotificationChannel.getImportance();
- int numTotalChannels = iNotificationManager.getNumNotificationChannelsForPackage(
+ int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
pkg, mAppUid, false /* includeDeleted */);
- if (mNotificationChannels.isEmpty()) {
+ if (mNumNotificationChannels == 0) {
throw new IllegalArgumentException("bindNotification requires at least one channel");
} else {
- if (mNotificationChannels.size() == 1) {
- mSingleNotificationChannel = mNotificationChannels.get(0);
- // Special behavior for the Default channel if no other channels have been defined.
- mIsSingleDefaultChannel =
- (mSingleNotificationChannel.getId()
- .equals(NotificationChannel.DEFAULT_CHANNEL_ID) &&
- numTotalChannels <= 1);
- } else {
- mSingleNotificationChannel = null;
- mIsSingleDefaultChannel = false;
- }
+ // Special behavior for the Default channel if no other channels have been defined.
+ mIsSingleDefaultChannel = mNumNotificationChannels == 1
+ && mSingleNotificationChannel.getId()
+ .equals(NotificationChannel.DEFAULT_CHANNEL_ID)
+ && numTotalChannels <= 1;
}
- boolean nonBlockable = false;
try {
final PackageInfo pkgInfo = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES);
if (Utils.isSystemPackage(getResources(), pm, pkgInfo)) {
- final int numChannels = mNotificationChannels.size();
- for (int i = 0; i < numChannels; i++) {
- final NotificationChannel notificationChannel = mNotificationChannels.get(i);
- // If any of the system channels is not blockable, the bundle is nonblockable
- if (!notificationChannel.isBlockableSystem()) {
- nonBlockable = true;
- break;
- }
+ if (mSingleNotificationChannel != null
+ && !mSingleNotificationChannel.isBlockableSystem()) {
+ mNonblockable = true;
}
}
} catch (PackageManager.NameNotFoundException e) {
// unlikely.
}
if (nonBlockablePkgs != null) {
- nonBlockable |= nonBlockablePkgs.contains(pkg);
+ mNonblockable |= nonBlockablePkgs.contains(pkg);
}
+ mNonblockable |= (mNumNotificationChannels > 1);
- String channelsDescText;
- mNumChannelsView = findViewById(R.id.num_channels_desc);
- if (nonBlockable) {
- channelsDescText = mContext.getString(R.string.notification_unblockable_desc);
- } else if (mIsSingleDefaultChannel) {
- channelsDescText = mContext.getString(R.string.notification_default_channel_desc);
- } else {
- switch (mNotificationChannels.size()) {
- case 1:
- channelsDescText = String.format(mContext.getResources().getQuantityString(
- R.plurals.notification_num_channels_desc, numTotalChannels),
- numTotalChannels);
- break;
- case 2:
- channelsDescText = mContext.getString(
- R.string.notification_channels_list_desc_2,
- mNotificationChannels.get(0).getName(),
- mNotificationChannels.get(1).getName());
- break;
- default:
- final int numOthers = mNotificationChannels.size() - 2;
- channelsDescText = String.format(
- mContext.getResources().getQuantityString(
- R.plurals.notification_channels_list_desc_2_and_others,
- numOthers),
- mNotificationChannels.get(0).getName(),
- mNotificationChannels.get(1).getName(),
- numOthers);
+ bindHeader();
+ bindPrompt();
+ bindButtons();
+ }
+
+ private void bindHeader() throws RemoteException {
+ // Package name
+ Drawable pkgicon = null;
+ ApplicationInfo info;
+ try {
+ info = mPm.getApplicationInfo(mPkg,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+ if (info != null) {
+ mAppUid = mSbn.getUid();
+ mAppName = String.valueOf(mPm.getApplicationLabel(info));
+ pkgicon = mPm.getApplicationIcon(info);
}
+ } catch (PackageManager.NameNotFoundException e) {
+ // app is gone, just show package name and generic icon
+ pkgicon = mPm.getDefaultActivityIcon();
}
- mNumChannelsView.setText(channelsDescText);
-
- if (mSingleNotificationChannel == null) {
- // Multiple channels don't use a channel name for the title.
- channelNameText = mContext.getString(R.string.notification_num_channels,
- mNotificationChannels.size());
- } else if (mIsSingleDefaultChannel || nonBlockable) {
- // If this is the default channel or the app is unblockable,
- // don't use our channel-specific text.
- channelNameText = mContext.getString(R.string.notification_header_default_channel);
- } else {
- channelNameText = mSingleNotificationChannel.getName();
- }
+ ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
((TextView) findViewById(R.id.pkgname)).setText(mAppName);
- ((TextView) findViewById(R.id.channel_name)).setText(channelNameText);
// Set group information if this channel has an associated group.
CharSequence groupName = null;
if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
final NotificationChannelGroup notificationChannelGroup =
- iNotificationManager.getNotificationChannelGroupForPackage(
- mSingleNotificationChannel.getGroup(), pkg, mAppUid);
+ mINotificationManager.getNotificationChannelGroupForPackage(
+ mSingleNotificationChannel.getGroup(), mPkg, mAppUid);
if (notificationChannelGroup != null) {
groupName = notificationChannelGroup.getName();
}
}
- TextView groupNameView = ((TextView) findViewById(R.id.group_name));
- TextView groupDividerView = ((TextView) findViewById(R.id.pkg_group_divider));
+ TextView groupNameView = findViewById(R.id.group_name);
+ TextView groupDividerView = findViewById(R.id.pkg_group_divider);
if (groupName != null) {
groupNameView.setText(groupName);
groupNameView.setVisibility(View.VISIBLE);
@@ -242,53 +210,55 @@
groupDividerView.setVisibility(View.GONE);
}
- bindButtons(nonBlockable);
-
- // Top-level importance group
- mChannelDisabledView = findViewById(R.id.channel_disabled);
- updateSecondaryText();
-
// Settings button.
- final TextView settingsButton = (TextView) findViewById(R.id.more_settings);
- if (mAppUid >= 0 && onSettingsClick != null) {
+ final View settingsButton = findViewById(R.id.info);
+ if (mAppUid >= 0 && mOnSettingsClickListener != null) {
settingsButton.setVisibility(View.VISIBLE);
final int appUidF = mAppUid;
settingsButton.setOnClickListener(
(View view) -> {
- onSettingsClick.onClick(view, mSingleNotificationChannel, appUidF);
+ mOnSettingsClickListener.onClick(view,
+ mNumNotificationChannels > 1 ? null : mSingleNotificationChannel,
+ appUidF);
});
- if (numTotalChannels <= 1 || nonBlockable) {
- settingsButton.setText(R.string.notification_more_settings);
- } else {
- settingsButton.setText(R.string.notification_all_categories);
- }
} else {
settingsButton.setVisibility(View.GONE);
}
+ }
- // Done button.
- final TextView doneButton = (TextView) findViewById(R.id.done);
- doneButton.setText(R.string.notification_done);
- doneButton.setOnClickListener(onDoneClick);
+ private void bindPrompt() {
+ final TextView channelName = findViewById(R.id.channel_name);
+ final TextView blockPrompt = findViewById(R.id.block_prompt);
+ if (mNonblockable) {
+ if (mIsSingleDefaultChannel || mNumNotificationChannels > 1) {
+ channelName.setVisibility(View.GONE);
+ } else {
+ channelName.setText(mSingleNotificationChannel.getName());
+ }
- // Optional settings link
- updateAppSettingsLink();
+ blockPrompt.setText(R.string.notification_unblockable_desc);
+ } else {
+ if (mIsSingleDefaultChannel || mNumNotificationChannels > 1) {
+ channelName.setVisibility(View.GONE);
+ blockPrompt.setText(R.string.inline_keep_showing_app);
+ } else {
+ channelName.setText(mSingleNotificationChannel.getName());
+ blockPrompt.setText(R.string.inline_keep_showing);
+ }
+ }
}
private boolean hasImportanceChanged() {
- return mSingleNotificationChannel != null &&
- mChannelEnabledSwitch != null &&
- mStartingUserImportance != getSelectedImportance();
+ return mSingleNotificationChannel != null && mStartingUserImportance != mChosenImportance;
}
private void saveImportance() {
- if (!hasImportanceChanged()) {
+ if (mNonblockable || !hasImportanceChanged()) {
return;
}
- final int selectedImportance = getSelectedImportance();
MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
- selectedImportance - mStartingUserImportance);
- mSingleNotificationChannel.setImportance(selectedImportance);
+ mChosenImportance - mStartingUserImportance);
+ mSingleNotificationChannel.setImportance(mChosenImportance);
mSingleNotificationChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
try {
mINotificationManager.updateNotificationChannelForPackage(
@@ -298,30 +268,78 @@
}
}
- private int getSelectedImportance() {
- if (!mChannelEnabledSwitch.isChecked()) {
- return IMPORTANCE_NONE;
+ private void bindButtons() {
+ View block = findViewById(R.id.block);
+ block.setOnClickListener(mOnStopNotifications);
+ TextView keep = findViewById(R.id.keep);
+ keep.setOnClickListener(mOnKeepShowing);
+ findViewById(R.id.undo).setOnClickListener(mOnUndo);
+
+ if (mNonblockable) {
+ keep.setText(R.string.notification_done);
+ block.setVisibility(GONE);
+ }
+
+ // app settings link
+ TextView settingsLinkView = findViewById(R.id.app_settings);
+ Intent settingsIntent = getAppSettingsIntent(mPm, mPkg, mSingleNotificationChannel,
+ mSbn.getId(), mSbn.getTag());
+ if (settingsIntent != null
+ && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
+ settingsLinkView.setVisibility(View.VISIBLE);
+ settingsLinkView.setText(mContext.getString(R.string.notification_app_settings,
+ mSbn.getNotification().getSettingsText()));
+ settingsLinkView.setOnClickListener((View view) -> {
+ mAppSettingsClickListener.onClick(view, settingsIntent);
+ });
} else {
- return mStartingUserImportance;
+ settingsLinkView.setVisibility(View.GONE);
}
}
- private void bindButtons(final boolean nonBlockable) {
- // Enabled Switch
- mChannelEnabledSwitch = (Switch) findViewById(R.id.channel_enabled_switch);
- mChannelEnabledSwitch.setChecked(
- mStartingUserImportance != IMPORTANCE_NONE);
- final boolean visible = !nonBlockable && mSingleNotificationChannel != null;
- mChannelEnabledSwitch.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+ private void swapContent(boolean showPrompt) {
+ if (mExpandAnimation != null) {
+ mExpandAnimation.cancel();
+ }
- // Callback when checked.
- mChannelEnabledSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
- if (mGutsContainer != null) {
- mGutsContainer.resetFalsingCheck();
+ if (showPrompt) {
+ mChosenImportance = mStartingUserImportance;
+ } else {
+ mChosenImportance = IMPORTANCE_NONE;
+ }
+
+ View prompt = findViewById(R.id.prompt);
+ View confirmation = findViewById(R.id.confirmation);
+ ObjectAnimator promptAnim = ObjectAnimator.ofFloat(prompt, View.ALPHA,
+ prompt.getAlpha(), showPrompt ? 1f : 0f);
+ promptAnim.setInterpolator(showPrompt ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
+ ObjectAnimator confirmAnim = ObjectAnimator.ofFloat(confirmation, View.ALPHA,
+ confirmation.getAlpha(), showPrompt ? 0f : 1f);
+ confirmAnim.setInterpolator(showPrompt ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN);
+
+ prompt.setVisibility(showPrompt ? VISIBLE : GONE);
+ confirmation.setVisibility(showPrompt ? GONE : VISIBLE);
+
+ mExpandAnimation = new AnimatorSet();
+ mExpandAnimation.playTogether(promptAnim, confirmAnim);
+ mExpandAnimation.setDuration(150);
+ mExpandAnimation.addListener(new AnimatorListenerAdapter() {
+ boolean cancelled = false;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ cancelled = true;
}
- updateSecondaryText();
- updateAppSettingsLink();
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!cancelled) {
+ prompt.setVisibility(showPrompt ? VISIBLE : GONE);
+ confirmation.setVisibility(showPrompt ? GONE : VISIBLE);
+ }
+ }
});
+ mExpandAnimation.start();
}
@Override
@@ -339,35 +357,6 @@
}
}
- private void updateSecondaryText() {
- final boolean disabled = mSingleNotificationChannel != null &&
- getSelectedImportance() == IMPORTANCE_NONE;
- if (disabled) {
- mChannelDisabledView.setVisibility(View.VISIBLE);
- mNumChannelsView.setVisibility(View.GONE);
- } else {
- mChannelDisabledView.setVisibility(View.GONE);
- mNumChannelsView.setVisibility(mIsSingleDefaultChannel ? View.INVISIBLE : View.VISIBLE);
- }
- }
-
- private void updateAppSettingsLink() {
- mSettingsLinkView = findViewById(R.id.app_settings);
- Intent settingsIntent = getAppSettingsIntent(mPm, mPkg, mSingleNotificationChannel,
- mSbn.getId(), mSbn.getTag());
- if (settingsIntent != null && getSelectedImportance() != IMPORTANCE_NONE
- && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
- mSettingsLinkView.setVisibility(View.VISIBLE);
- mSettingsLinkView.setText(mContext.getString(R.string.notification_app_settings,
- mSbn.getNotification().getSettingsText()));
- mSettingsLinkView.setOnClickListener((View view) -> {
- mAppSettingsClickListener.onClick(view, settingsIntent);
- });
- } else {
- mSettingsLinkView.setVisibility(View.GONE);
- }
- }
-
private Intent getAppSettingsIntent(PackageManager pm, String packageName,
NotificationChannel channel, int id, String tag) {
Intent intent = new Intent(Intent.ACTION_MAIN)
@@ -390,6 +379,18 @@
return intent;
}
+ private void closeControls(View v) {
+ int[] parentLoc = new int[2];
+ int[] targetLoc = new int[2];
+ mGutsContainer.getLocationOnScreen(parentLoc);
+ v.getLocationOnScreen(targetLoc);
+ final int centerX = v.getWidth() / 2;
+ final int centerY = v.getHeight() / 2;
+ final int x = targetLoc[0] - parentLoc[0] + centerX;
+ final int y = targetLoc[1] - parentLoc[1] + centerY;
+ mGutsContainer.closeControls(x, y, false /* save */, false /* force */);
+ }
+
@Override
public void setGutsParent(NotificationGuts guts) {
mGutsContainer = guts;
@@ -397,7 +398,7 @@
@Override
public boolean willBeRemoved() {
- return mChannelEnabledSwitch != null && !mChannelEnabledSwitch.isChecked();
+ return hasImportanceChanged();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
index b2604fe..037eeb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
@@ -176,8 +176,6 @@
final Resources res = mContext.getResources();
mHorizSpaceForIcon = res.getDimensionPixelSize(R.dimen.notification_menu_icon_size);
mVertSpaceForIcons = res.getDimensionPixelSize(R.dimen.notification_min_height);
- mSidePadding = res.getDimensionPixelSize(R.dimen.notification_lockscreen_side_paddings);
- mIconPadding = res.getDimensionPixelSize(R.dimen.notification_menu_icon_padding);
mMenuItems.clear();
// Construct the menu items based on the notification
if (mParent != null && mParent.getStatusBarNotification() != null) {
@@ -498,8 +496,8 @@
final int count = mMenuContainer.getChildCount();
for (int i = 0; i < count; i++) {
final View v = mMenuContainer.getChildAt(i);
- final float left = mSidePadding + i * mHorizSpaceForIcon;
- final float right = mParent.getWidth() - (mHorizSpaceForIcon * (i + 1)) - mSidePadding;
+ final float left = i * mHorizSpaceForIcon;
+ final float right = mParent.getWidth() - (mHorizSpaceForIcon * (i + 1));
v.setX(showOnLeft ? left : right);
}
mOnLeft = showOnLeft;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
index 6cbbd6c..e5a311d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
@@ -17,11 +17,15 @@
package com.android.systemui.statusbar.car;
import android.content.Context;
+import android.graphics.Canvas;
import android.util.AttributeSet;
+import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import com.android.systemui.R;
+import com.android.systemui.plugins.statusbar.phone.NavGesture;
+import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
import com.android.systemui.statusbar.phone.NavigationBarView;
/**
@@ -72,4 +76,68 @@
// Calling setNavigationIconHints in the base class will result in a NPE as the car
// navigation bar does not have a back button.
}
+
+ @Override
+ public void onPluginConnected(NavGesture plugin, Context context) {
+ // set to null version of the plugin ignoring incoming arg.
+ super.onPluginConnected(new NullNavGesture(), context);
+ }
+
+ @Override
+ public void onPluginDisconnected(NavGesture plugin) {
+ // reinstall the null nav gesture plugin
+ super.onPluginConnected(new NullNavGesture(), getContext());
+ }
+
+ /**
+ * Null object pattern to work around expectations of the base class.
+ * This is a temporary solution to have the car system ui working.
+ * Already underway is a refactor of they car sys ui as to not use this class
+ * hierarchy.
+ */
+ private static class NullNavGesture implements NavGesture {
+ @Override
+ public GestureHelper getGestureHelper() {
+ return new GestureHelper() {
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return false;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ return false;
+ }
+
+ @Override
+ public void setBarState(boolean vertical, boolean isRtl) {
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ }
+
+ @Override
+ public void onDarkIntensityChange(float intensity) {
+ }
+
+ @Override
+ public void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ }
+ };
+ }
+
+ @Override
+ public int getVersion() {
+ return 0;
+ }
+
+ @Override
+ public void onCreate(Context sysuiContext, Context pluginContext) {
+ }
+
+ @Override
+ public void onDestroy() {
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
index 27defca..113118a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
@@ -26,9 +26,7 @@
import com.android.internal.widget.MessagingLinearLayout;
import com.android.internal.widget.MessagingMessage;
import com.android.internal.widget.MessagingPropertyAnimator;
-import com.android.internal.widget.ViewClippingUtil;
import com.android.systemui.Interpolators;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
import java.util.ArrayList;
import java.util.HashMap;
@@ -157,8 +155,8 @@
setClippingDeactivated(child, true);
}
appear(ownGroup.getAvatar(), transformationAmount);
- appear(ownGroup.getSender(), transformationAmount);
- setClippingDeactivated(ownGroup.getSender(), true);
+ appear(ownGroup.getSenderView(), transformationAmount);
+ setClippingDeactivated(ownGroup.getSenderView(), true);
setClippingDeactivated(ownGroup.getAvatar(), true);
}
@@ -170,7 +168,7 @@
} else {
relativeOffset = (1.0f - transformationAmount) * mRelativeTranslationOffset;
}
- if (ownGroup.getSender().getVisibility() != View.GONE) {
+ if (ownGroup.getSenderView().getVisibility() != View.GONE) {
relativeOffset *= 0.5f;
}
ownGroup.getMessageContainer().setTranslationY(relativeOffset);
@@ -188,8 +186,8 @@
setClippingDeactivated(child, true);
}
disappear(ownGroup.getAvatar(), transformationAmount);
- disappear(ownGroup.getSender(), transformationAmount);
- setClippingDeactivated(ownGroup.getSender(), true);
+ disappear(ownGroup.getSenderView(), transformationAmount);
+ setClippingDeactivated(ownGroup.getSenderView(), true);
setClippingDeactivated(ownGroup.getAvatar(), true);
}
@@ -226,7 +224,7 @@
private void transformGroups(MessagingGroup ownGroup, MessagingGroup otherGroup,
float transformationAmount, boolean to) {
- transformView(transformationAmount, to, ownGroup.getSender(), otherGroup.getSender(),
+ transformView(transformationAmount, to, ownGroup.getSenderView(), otherGroup.getSenderView(),
true /* sameAsAny */);
transformView(transformationAmount, to, ownGroup.getAvatar(), otherGroup.getAvatar(),
true /* sameAsAny */);
@@ -345,7 +343,7 @@
setVisible(child, visible, force);
}
setVisible(ownGroup.getAvatar(), visible, force);
- setVisible(ownGroup.getSender(), visible, force);
+ setVisible(ownGroup.getSenderView(), visible, force);
}
}
}
@@ -376,9 +374,9 @@
setClippingDeactivated(child, false);
}
resetTransformedView(ownGroup.getAvatar());
- resetTransformedView(ownGroup.getSender());
+ resetTransformedView(ownGroup.getSenderView());
setClippingDeactivated(ownGroup.getAvatar(), false);
- setClippingDeactivated(ownGroup.getSender(), false);
+ setClippingDeactivated(ownGroup.getSenderView(), false);
ownGroup.setTranslationY(0);
ownGroup.getMessageContainer().setTranslationY(0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
index 66682e4..adc0914 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
-import android.os.Build;
import android.view.View;
import com.android.systemui.R;
@@ -38,7 +37,6 @@
private final Paint mGreyPaint = new Paint();
private boolean mIsLegacy;
private int mLegacyColor;
- private boolean mBeforeP;
protected NotificationCustomViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
super(ctx, view, row);
@@ -119,15 +117,7 @@
}
@Override
- public boolean shouldClipToSidePaddings() {
- // Before P we ensure that they are now drawing inside out content bounds since we inset
- // the view. If they target P, then we don't have that guarantee and we need to be safe.
- return !mBeforeP;
- }
-
- @Override
- public void onContentUpdated(ExpandableNotificationRow row) {
- super.onContentUpdated(row);
- mBeforeP = row.getEntry().targetSdk < Build.VERSION_CODES.P;
+ public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
+ return true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java
index 060e6d6..548f006 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java
@@ -62,7 +62,7 @@
}
@Override
- public boolean shouldClipToSidePaddings() {
+ public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
return true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index e07112f..d463eae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -266,8 +266,12 @@
}
@Override
- public boolean shouldClipToSidePaddings() {
- return mActionsContainer != null && mActionsContainer.getVisibility() != View.GONE;
+ public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
+ if (super.shouldClipToRounding(topRounded, bottomRounded)) {
+ return true;
+ }
+ return bottomRounded && mActionsContainer != null
+ && mActionsContainer.getVisibility() != View.GONE;
}
private void updateActionOffset() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index 8a767bb..17eb4c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -195,7 +195,7 @@
return 0;
}
- public boolean shouldClipToSidePaddings() {
+ public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
return false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
index a83e659..9b123cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
@@ -14,7 +14,6 @@
package com.android.systemui.statusbar.phone;
-import android.graphics.drawable.Drawable;
import android.view.View;
import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface;
@@ -39,6 +38,7 @@
private Integer mAlpha;
private Float mDarkIntensity;
private Integer mVisibility = -1;
+ private Boolean mDelayTouchFeedback;
private KeyButtonDrawable mImageDrawable;
private View mCurrentView;
private boolean mVertical;
@@ -71,10 +71,10 @@
if (mImageDrawable != null) {
((ButtonInterface) view).setImageDrawable(mImageDrawable);
}
-
- if (view instanceof ButtonInterface) {
- ((ButtonInterface) view).setVertical(mVertical);
+ if (mDelayTouchFeedback != null) {
+ ((ButtonInterface) view).setDelayTouchFeedback(mDelayTouchFeedback);
}
+ ((ButtonInterface) view).setVertical(mVertical);
}
public int getId() {
@@ -89,6 +89,10 @@
return mAlpha != null ? mAlpha : 1;
}
+ public KeyButtonDrawable getImageDrawable() {
+ return mImageDrawable;
+ }
+
public void setImageDrawable(KeyButtonDrawable drawable) {
mImageDrawable = drawable;
final int N = mViews.size();
@@ -130,6 +134,14 @@
}
}
+ public void setDelayTouchFeedback(boolean delay) {
+ mDelayTouchFeedback = delay;
+ final int N = mViews.size();
+ for (int i = 0; i < N; i++) {
+ ((ButtonInterface) mViews.get(i)).setDelayTouchFeedback(delay);
+ }
+ }
+
public void setOnClickListener(View.OnClickListener clickListener) {
mClickListener = clickListener;
final int N = mViews.size();
@@ -162,6 +174,14 @@
}
}
+ public void setClickable(boolean clickable) {
+ abortCurrentGesture();
+ final int N = mViews.size();
+ for (int i = 0; i < N; i++) {
+ mViews.get(i).setClickable(clickable);
+ }
+ }
+
public ArrayList<View> getViews() {
return mViews;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 01b3b44..ca66e98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -51,6 +51,7 @@
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.MathUtils;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
@@ -166,6 +167,10 @@
private String mLeftButtonStr;
private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
private boolean mDozing;
+ private int mIndicationBottomMargin;
+ private int mIndicationBottomMarginAmbient;
+ private float mDarkAmount;
+ private int mBurnInXOffset;
public KeyguardBottomAreaView(Context context) {
this(context, null);
@@ -235,6 +240,10 @@
mEnterpriseDisclosure = findViewById(
R.id.keyguard_indication_enterprise_disclosure);
mIndicationText = findViewById(R.id.keyguard_indication_text);
+ mIndicationBottomMargin = getResources().getDimensionPixelSize(
+ R.dimen.keyguard_indication_margin_bottom);
+ mIndicationBottomMarginAmbient = getResources().getDimensionPixelSize(
+ R.dimen.keyguard_indication_margin_bottom_ambient);
updateCameraVisibility();
mUnlockMethodCache = UnlockMethodCache.getInstance(getContext());
mUnlockMethodCache.addListener(this);
@@ -303,11 +312,13 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- int indicationBottomMargin = getResources().getDimensionPixelSize(
+ mIndicationBottomMargin = getResources().getDimensionPixelSize(
R.dimen.keyguard_indication_margin_bottom);
+ mIndicationBottomMarginAmbient = getResources().getDimensionPixelSize(
+ R.dimen.keyguard_indication_margin_bottom_ambient);
MarginLayoutParams mlp = (MarginLayoutParams) mIndicationArea.getLayoutParams();
- if (mlp.bottomMargin != indicationBottomMargin) {
- mlp.bottomMargin = indicationBottomMargin;
+ if (mlp.bottomMargin != mIndicationBottomMargin) {
+ mlp.bottomMargin = mIndicationBottomMargin;
mIndicationArea.setLayoutParams(mlp);
}
@@ -543,6 +554,22 @@
}
}
+ public void setDarkAmount(float darkAmount) {
+ if (darkAmount == mDarkAmount) {
+ return;
+ }
+ mDarkAmount = darkAmount;
+ // Let's randomize the bottom margin every time we wake up to avoid burn-in.
+ if (darkAmount == 0) {
+ mIndicationBottomMarginAmbient = getResources().getDimensionPixelSize(
+ R.dimen.keyguard_indication_margin_bottom_ambient)
+ + (int) (Math.random() * mIndicationText.getTextSize());
+ }
+ mIndicationArea.setAlpha(MathUtils.lerp(1f, 0.7f, darkAmount));
+ mIndicationArea.setTranslationY(MathUtils.lerp(0,
+ mIndicationBottomMargin - mIndicationBottomMarginAmbient, darkAmount));
+ }
+
private static boolean isSuccessfulLaunch(int result) {
return result == ActivityManager.START_SUCCESS
|| result == ActivityManager.START_DELIVERED_TO_TOP
@@ -687,11 +714,6 @@
if (mRightAffordanceView.getVisibility() == View.VISIBLE) {
startFinishDozeAnimationElement(mRightAffordanceView, delay);
}
- mIndicationArea.setAlpha(0f);
- mIndicationArea.animate()
- .alpha(1f)
- .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
- .setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
}
private void startFinishDozeAnimationElement(View element, long delay) {
@@ -815,6 +837,22 @@
}
}
+ public void dozeTimeTick() {
+ if (mDarkAmount == 1) {
+ // Move indication every minute to avoid burn-in
+ final int dozeTranslation = mIndicationBottomMargin - mIndicationBottomMarginAmbient;
+ mIndicationArea.setTranslationY(dozeTranslation + (float) Math.random() * 5);
+ }
+ }
+
+ public void setBurnInXOffset(int burnInXOffset) {
+ if (mBurnInXOffset == burnInXOffset) {
+ return;
+ }
+ mBurnInXOffset = burnInXOffset;
+ mIndicationArea.setTranslationX(burnInXOffset);
+ }
+
private class DefaultLeftButton implements IntentButton {
private IconState mIconState = new IconState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index b71ebfd..699e8cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -131,6 +131,10 @@
mRoot.setVisibility(View.VISIBLE);
mKeyguardView.onResume();
showPromptReason(mBouncerPromptReason);
+ final CharSequence customMessage = mCallback.consumeCustomMessage();
+ if (customMessage != null) {
+ mKeyguardView.showErrorMessage(customMessage);
+ }
// We might still be collapsed and the view didn't have time to layout yet or still
// be small, let's wait on the predraw to do the animation in that case.
if (mKeyguardView.getHeight() != 0 && mKeyguardView.getHeight() != mStatusBarHeight) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 34486db..264f574 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -250,7 +250,7 @@
}
break;
case STATE_FACE_UNLOCK:
- iconRes = R.drawable.ic_account_circle;
+ iconRes = R.drawable.ic_face_unlock;
break;
case STATE_FINGERPRINT:
// If screen is off and device asleep, use the draw on animation so the first frame
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
index 7f4deb0..0f8d59b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
@@ -61,7 +61,7 @@
public void setWorkModeEnabled(boolean enableWorkMode) {
synchronized (mProfiles) {
for (UserInfo ui : mProfiles) {
- if (!mUserManager.trySetQuietModeEnabled(!enableWorkMode, UserHandle.of(ui.id))) {
+ if (!mUserManager.requestQuietModeEnabled(!enableWorkMode, UserHandle.of(ui.id))) {
StatusBarManager statusBarManager = (StatusBarManager) mContext
.getSystemService(android.app.Service.STATUS_BAR_SERVICE);
statusBarManager.collapsePanels();
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 695168e..70ec45e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -24,6 +24,10 @@
import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
@@ -39,6 +43,7 @@
import android.database.ContentObserver;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.graphics.drawable.AnimatedVectorDrawable;
import android.inputmethodservice.InputMethodService;
import android.os.Binder;
import android.os.Bundle;
@@ -70,17 +75,22 @@
import com.android.internal.util.LatencyTracker;
import com.android.systemui.Dependency;
import com.android.systemui.OverviewProxyService;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.statusbar.policy.KeyButtonDrawable;
import com.android.systemui.statusbar.policy.KeyButtonView;
+import com.android.systemui.statusbar.policy.RotationLockController;
import com.android.systemui.statusbar.stack.StackStateAnimator;
import java.io.FileDescriptor;
@@ -101,6 +111,8 @@
/** Allow some time inbetween the long press for back and recents. */
private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
+ private static final int ROTATE_SUGGESTION_TIMEOUT_MS = 4000;
+
protected NavigationBarView mNavigationBarView = null;
protected AssistManager mAssistManager;
@@ -130,6 +142,15 @@
public boolean mHomeBlockedThisTouch;
+ private int mLastRotationSuggestion;
+ private RotationLockController mRotationLockController;
+ private TaskStackListenerImpl mTaskStackListener;
+
+ private final Runnable mRemoveRotationProposal = () -> setRotateSuggestionButtonState(false);
+ private Animator mRotateShowAnimator;
+ private Animator mRotateHideAnimator;
+
+
// ----- Fragment Lifecycle Callbacks -----
@Override
@@ -163,6 +184,12 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+
+ mRotationLockController = Dependency.get(RotationLockController.class);
+
+ // Register the task stack listener
+ mTaskStackListener = new TaskStackListenerImpl();
+ ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
}
@Override
@@ -178,6 +205,9 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+
+ // Unregister the task stack listener
+ ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
}
@Override
@@ -304,6 +334,92 @@
}
}
+ @Override
+ public void onRotationProposal(final int rotation) {
+ // This method will only be called if rotation is valid but will include proposals for the
+ // current system rotation
+ Handler h = getView().getHandler();
+ if (rotation == mWindowManager.getDefaultDisplay().getRotation()) {
+ // Use this as a signal to remove any current suggestions
+ h.removeCallbacks(mRemoveRotationProposal);
+ setRotateSuggestionButtonState(false);
+ } else {
+ mLastRotationSuggestion = rotation; // Remember rotation for click
+ setRotateSuggestionButtonState(true);
+ h.removeCallbacks(mRemoveRotationProposal); // Stop any pending removal
+ h.postDelayed(mRemoveRotationProposal,
+ ROTATE_SUGGESTION_TIMEOUT_MS); // Schedule timeout
+ }
+ }
+
+ public void setRotateSuggestionButtonState(final boolean visible) {
+ setRotateSuggestionButtonState(visible, false);
+ }
+
+ public void setRotateSuggestionButtonState(final boolean visible, final boolean skipAnim) {
+ ButtonDispatcher rotBtn = mNavigationBarView.getRotateSuggestionButton();
+ boolean currentlyVisible = rotBtn.getVisibility() == View.VISIBLE;
+
+ // Rerun a show animation to indicate change but don't rerun a hide animation
+ if (!visible && !currentlyVisible) return;
+
+ View currentView = mNavigationBarView.getRotateSuggestionButton().getCurrentView();
+ if (currentView == null) return;
+
+ KeyButtonDrawable kbd = mNavigationBarView.getRotateSuggestionButton().getImageDrawable();
+ if (kbd == null) return;
+
+ AnimatedVectorDrawable animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0);
+ if (visible) { // Appear and change
+ rotBtn.setVisibility(View.VISIBLE);
+
+ if (skipAnim) {
+ currentView.setAlpha(1f);
+ return;
+ }
+
+ // Start a new animation if running
+ if (mRotateShowAnimator != null) mRotateShowAnimator.pause();
+ if (mRotateHideAnimator != null) mRotateHideAnimator.pause();
+
+ ObjectAnimator appearFade = ObjectAnimator.ofFloat(currentView, "alpha",
+ 0f, 1f);
+ appearFade.setDuration(100);
+ appearFade.setInterpolator(Interpolators.LINEAR);
+ mRotateShowAnimator = appearFade;
+ appearFade.start();
+
+ // Run the rotate icon's animation
+ animIcon.reset();
+ animIcon.start();
+ } else { // Hide
+
+ if (skipAnim) {
+ rotBtn.setVisibility(View.INVISIBLE);
+ return;
+ }
+
+ // Don't start any new hide animations if one is running
+ if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) return;
+ // Pause any active show animations but don't reset the AVD to avoid jumps
+ if (mRotateShowAnimator != null) mRotateShowAnimator.pause();
+
+ ObjectAnimator fadeOut = ObjectAnimator.ofFloat(currentView, "alpha",
+ 0f);
+ fadeOut.setDuration(100);
+ fadeOut.setInterpolator(Interpolators.LINEAR);
+ fadeOut.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ rotBtn.setVisibility(View.INVISIBLE);
+ }
+ });
+
+ mRotateHideAnimator = fadeOut;
+ fadeOut.start();
+ }
+ }
+
// Injected from StatusBar at creation.
public void setCurrentSysuiVisibility(int systemUiVisibility) {
mSystemUiVisibility = systemUiVisibility;
@@ -406,6 +522,9 @@
accessibilityButton.setOnClickListener(this::onAccessibilityClick);
accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
updateAccessibilityServicesState(mAccessibilityManager);
+
+ ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton();
+ rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick);
}
private boolean onHomeTouch(View v, MotionEvent event) {
@@ -598,6 +717,10 @@
mNavigationBarView.setAccessibilityButtonState(showAccessibilityButton, targetSelection);
}
+ private void onRotateSuggestionClick(View v) {
+ mRotationLockController.setRotationLockedAtAngle(true, mLastRotationSuggestion);
+ }
+
// ----- Methods that StatusBar talks to (should be minimized) -----
public void setLightBarController(LightBarController lightBarController) {
@@ -646,6 +769,13 @@
private final Stub mRotationWatcher = new Stub() {
@Override
public void onRotationChanged(int rotation) throws RemoteException {
+ // If the screen rotation changes while locked, update lock rotation to flow with
+ // new screen rotation and hide any showing suggestions.
+ if (mRotationLockController.isRotationLocked()) {
+ mRotationLockController.setRotationLockedAtAngle(true, rotation);
+ setRotateSuggestionButtonState(false, true);
+ }
+
// We need this to be scheduled as early as possible to beat the redrawing of
// window in response to the orientation change.
Handler h = getView().getHandler();
@@ -671,6 +801,31 @@
}
};
+ class TaskStackListenerImpl extends SysUiTaskStackChangeListener {
+ // Invalidate any rotation suggestion on task change or activity orientation change
+ // Note: all callbacks happen on main thread
+
+ @Override
+ public void onTaskStackChanged() {
+ setRotateSuggestionButtonState(false);
+ }
+
+ @Override
+ public void onTaskRemoved(int taskId) {
+ setRotateSuggestionButtonState(false);
+ }
+
+ @Override
+ public void onTaskMovedToFront(int taskId) {
+ setRotateSuggestionButtonState(false);
+ }
+
+ @Override
+ public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) {
+ setRotateSuggestionButtonState(false);
+ }
+ }
+
public static View create(Context context, FragmentListener listener) {
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index 6f636aa..4faa84a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -19,15 +19,14 @@
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.RemoteException;
import android.util.Log;
-import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
-import android.view.ViewConfiguration;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
@@ -37,6 +36,7 @@
import com.android.systemui.RecentsComponent;
import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.tuner.TunerService;
@@ -72,6 +72,7 @@
private NavigationBarView mNavigationBarView;
private boolean mIsVertical;
+ private final QuickScrubController mQuickScrubController;
private final int mScrollTouchSlop;
private final Matrix mTransformGlobalMatrix = new Matrix();
private final Matrix mTransformLocalMatrix = new Matrix();
@@ -89,6 +90,7 @@
mContext = context;
Resources r = context.getResources();
mScrollTouchSlop = r.getDimensionPixelSize(R.dimen.navigation_bar_min_swipe_distance);
+ mQuickScrubController = new QuickScrubController(context);
Dependency.get(TunerService.class).addTunable(this, KEY_DOCK_WINDOW_GESTURE);
}
@@ -101,10 +103,12 @@
mRecentsComponent = recentsComponent;
mDivider = divider;
mNavigationBarView = navigationBarView;
+ mQuickScrubController.setComponents(mNavigationBarView);
}
public void setBarState(boolean isVertical, boolean isRTL) {
mIsVertical = isVertical;
+ mQuickScrubController.setBarState(isVertical, isRTL);
}
private boolean proxyMotionEvents(MotionEvent event) {
@@ -126,7 +130,6 @@
public boolean onInterceptTouchEvent(MotionEvent event) {
int action = event.getAction();
- boolean result = false;
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
mTouchDownX = (int) event.getX();
@@ -137,24 +140,26 @@
mNavigationBarView.transformMatrixToLocal(mTransformLocalMatrix);
break;
}
- case MotionEvent.ACTION_MOVE: {
- int x = (int) event.getX();
- int y = (int) event.getY();
- int xDiff = Math.abs(x - mTouchDownX);
- int yDiff = Math.abs(y - mTouchDownY);
- boolean exceededTouchSlop = xDiff > mScrollTouchSlop && xDiff > yDiff
- || yDiff > mScrollTouchSlop && yDiff > xDiff;
- if (exceededTouchSlop) {
- result = true;
- }
- break;
- }
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- break;
}
- proxyMotionEvents(event);
- return result || (mDockWindowEnabled && interceptDockWindowEvent(event));
+ if (!mQuickScrubController.onInterceptTouchEvent(event)) {
+ proxyMotionEvents(event);
+ return false;
+ }
+ return (mDockWindowEnabled && interceptDockWindowEvent(event));
+ }
+
+ public void onDraw(Canvas canvas) {
+ if (mOverviewEventSender.getProxy() != null) {
+ mQuickScrubController.onDraw(canvas);
+ }
+ }
+
+ public void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ mQuickScrubController.onLayout(changed, left, top, right, bottom);
+ }
+
+ public void onDarkIntensityChange(float intensity) {
+ mQuickScrubController.onDarkIntensityChange(intensity);
}
private boolean interceptDockWindowEvent(MotionEvent event) {
@@ -294,7 +299,7 @@
}
public boolean onTouchEvent(MotionEvent event) {
- boolean result = proxyMotionEvents(event);
+ boolean result = mQuickScrubController.onTouchEvent(event) || proxyMotionEvents(event);
if (mDockWindowEnabled) {
result |= handleDockWindowEvent(event);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index 4e79314b..b8b309b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -63,6 +63,7 @@
public static final String RECENT = "recent";
public static final String NAVSPACE = "space";
public static final String CLIPBOARD = "clipboard";
+ public static final String ROTATE = "rotate";
public static final String KEY = "key";
public static final String LEFT = "left";
public static final String RIGHT = "right";
@@ -311,7 +312,7 @@
View v = null;
String button = extractButton(buttonSpec);
if (LEFT.equals(button)) {
- String s = Dependency.get(TunerService.class).getValue(NAV_BAR_LEFT, NAVSPACE);
+ String s = Dependency.get(TunerService.class).getValue(NAV_BAR_LEFT, ROTATE);
button = extractButton(s);
} else if (RIGHT.equals(button)) {
String s = Dependency.get(TunerService.class).getValue(NAV_BAR_RIGHT, MENU_IME);
@@ -334,6 +335,8 @@
v = inflater.inflate(R.layout.nav_key_space, parent, false);
} else if (CLIPBOARD.equals(button)) {
v = inflater.inflate(R.layout.clipboard, parent, false);
+ } else if (ROTATE.equals(button)) {
+ v = inflater.inflate(R.layout.rotate_suggestion, parent, false);
} else if (button.startsWith(KEY)) {
String uri = extractImage(button);
int code = extractKeycode(button);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index bd6421c..b113675 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -138,6 +138,7 @@
if (mAutoDim) {
applyLightsOut(false, true);
}
+ mView.onDarkIntensityChange(darkIntensity);
}
private final View.OnTouchListener mLightsOutListener = new View.OnTouchListener() {
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 392581d..059ce92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -26,11 +26,13 @@
import android.app.StatusBarManager;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
+import android.support.annotation.ColorInt;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
@@ -56,6 +58,7 @@
import com.android.systemui.plugins.statusbar.phone.NavGesture;
import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.statusbar.policy.TintedKeyButtonDrawable;
import com.android.systemui.statusbar.policy.DeadZone;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
@@ -94,6 +97,7 @@
private KeyButtonDrawable mImeIcon;
private KeyButtonDrawable mMenuIcon;
private KeyButtonDrawable mAccessibilityIcon;
+ private KeyButtonDrawable mRotateSuggestionIcon;
private GestureHelper mGestureHelper;
private DeadZone mDeadZone;
@@ -229,6 +233,9 @@
mButtonDispatchers.put(R.id.ime_switcher, new ButtonDispatcher(R.id.ime_switcher));
mButtonDispatchers.put(R.id.accessibility_button,
new ButtonDispatcher(R.id.accessibility_button));
+ mButtonDispatchers.put(R.id.rotate_suggestion,
+ new ButtonDispatcher(R.id.rotate_suggestion));
+
mOverviewProxyService = Dependency.get(OverviewProxyService.class);
}
@@ -305,6 +312,10 @@
return mButtonDispatchers.get(R.id.accessibility_button);
}
+ public ButtonDispatcher getRotateSuggestionButton() {
+ return mButtonDispatchers.get(R.id.rotate_suggestion);
+ }
+
public SparseArray<ButtonDispatcher> getButtonDispatchers() {
return mButtonDispatchers;
}
@@ -349,6 +360,11 @@
mImeIcon = getDrawable(darkContext, lightContext,
R.drawable.ic_ime_switcher_default, R.drawable.ic_ime_switcher_default);
+ int lightColor = Utils.getColorAttr(lightContext, R.attr.singleToneColor);
+ int darkColor = Utils.getColorAttr(darkContext, R.attr.singleToneColor);
+ mRotateSuggestionIcon = getDrawable(ctx, R.drawable.ic_sysbar_rotate_button,
+ lightColor, darkColor);
+
if (ALTERNATE_CAR_MODE_UI) {
updateCarModeIcons(ctx);
}
@@ -366,6 +382,11 @@
darkContext.getDrawable(darkIcon));
}
+ private KeyButtonDrawable getDrawable(Context ctx, @DrawableRes int icon,
+ @ColorInt int lightColor, @ColorInt int darkColor) {
+ return TintedKeyButtonDrawable.create(ctx.getDrawable(icon), lightColor, darkColor);
+ }
+
@Override
public void setLayoutDirection(int layoutDirection) {
// Reload all the icons
@@ -439,6 +460,8 @@
setAccessibilityButtonState(mShowAccessibilityButton, mLongClickableAccessibilityButton);
getAccessibilityButton().setImageDrawable(mAccessibilityIcon);
+ getRotateSuggestionButton().setImageDrawable(mRotateSuggestionIcon);
+
setDisabledFlags(mDisabledFlags, true);
mBarTransitions.reapplyDarkIntensity();
@@ -603,6 +626,24 @@
updateRotatedViews();
}
+ public void onDarkIntensityChange(float intensity) {
+ if (mGestureHelper != null) {
+ mGestureHelper.onDarkIntensityChange(intensity);
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ mGestureHelper.onDraw(canvas);
+ super.onDraw(canvas);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ mGestureHelper.onLayout(changed, left, top, right, bottom);
+ }
+
private void updateRotatedViews() {
mRotatedViews[Surface.ROTATION_0] =
mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index f0bd1f9..0cc7f5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -309,7 +309,7 @@
mIndicationBottomPadding = getResources().getDimensionPixelSize(
R.dimen.keyguard_indication_bottom_padding);
mQsNotificationTopPadding = getResources().getDimensionPixelSize(
- R.dimen.qs_notification_keyguard_padding);
+ R.dimen.qs_notification_padding);
}
public void updateResources() {
@@ -451,7 +451,8 @@
boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending();
int stackScrollerPadding;
if (mStatusBarState != StatusBarState.KEYGUARD) {
- stackScrollerPadding = (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight;
+ stackScrollerPadding = (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight
+ + mQsNotificationTopPadding;
mTopPaddingAdjustment = 0;
} else {
mClockPositionAlgorithm.setup(
@@ -477,6 +478,7 @@
}
mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
mNotificationStackScroller.setDarkShelfOffsetX(mClockPositionResult.clockX);
+ mKeyguardBottomArea.setBurnInXOffset(mClockPositionResult.clockX);
requestScrollerTopPaddingUpdate(animate);
}
@@ -1381,7 +1383,7 @@
mNotificationStackScroller.getIntrinsicPadding(),
mQsMaxExpansionHeight + mQsNotificationTopPadding);
} else {
- return mQsExpansionHeight;
+ return mQsExpansionHeight + mQsNotificationTopPadding;
}
}
@@ -2607,7 +2609,8 @@
private void setDarkAmount(float amount) {
mDarkAmount = amount;
- mKeyguardStatusView.setDark(mDarkAmount);
+ mKeyguardStatusView.setDarkAmount(mDarkAmount);
+ mKeyguardBottomArea.setDarkAmount(mDarkAmount);
positionClockAndNotifications();
}
@@ -2629,8 +2632,9 @@
}
}
- public void refreshTime() {
+ public void dozeTimeTick() {
mKeyguardStatusView.refreshTime();
+ mKeyguardBottomArea.dozeTimeTick();
if (mDarkAmount > 0) {
positionClockAndNotifications();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
new file mode 100644
index 0000000..afd2236
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+import android.view.Display;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.support.annotation.DimenRes;
+import com.android.systemui.Dependency;
+import com.android.systemui.OverviewProxyService;
+import com.android.systemui.R;
+import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
+import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.recents.utilities.Utilities;
+
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+
+/**
+ * Class to detect gestures on the navigation bar and implement quick scrub and switch.
+ */
+public class QuickScrubController extends GestureDetector.SimpleOnGestureListener implements
+ GestureHelper {
+
+ private static final String TAG = "QuickScrubController";
+ private static final int QUICK_SWITCH_FLING_VELOCITY = 0;
+ private static final int ANIM_DURATION_MS = 200;
+ private static final long LONG_PRESS_DELAY_MS = 150;
+
+ /**
+ * For quick step, set a damping value to allow the button to stick closer its origin position
+ * when dragging before quick scrub is active.
+ */
+ private static final int SWITCH_STICKINESS = 4;
+
+ private NavigationBarView mNavigationBarView;
+ private GestureDetector mGestureDetector;
+
+ private boolean mDraggingActive;
+ private boolean mQuickScrubActive;
+ private float mDownOffset;
+ private float mTranslation;
+ private int mTouchDownX;
+ private int mTouchDownY;
+ private boolean mDragPositive;
+ private boolean mIsVertical;
+ private boolean mIsRTL;
+ private float mMaxTrackPaintAlpha;
+
+ private final Handler mHandler = new Handler();
+ private final Interpolator mQuickScrubEndInterpolator = new DecelerateInterpolator();
+ private final Rect mTrackRect = new Rect();
+ private final Rect mHomeButtonRect = new Rect();
+ private final Paint mTrackPaint = new Paint();
+ private final int mScrollTouchSlop;
+ private final OverviewProxyService mOverviewEventSender;
+ private final Display mDisplay;
+ private final int mTrackThickness;
+ private final int mTrackPadding;
+ private final ValueAnimator mTrackAnimator;
+ private final ValueAnimator mButtonAnimator;
+ private final AnimatorSet mQuickScrubEndAnimator;
+ private final Context mContext;
+
+ private final AnimatorUpdateListener mTrackAnimatorListener = valueAnimator -> {
+ mTrackPaint.setAlpha(Math.round((float) valueAnimator.getAnimatedValue() * 255));
+ mNavigationBarView.invalidate();
+ };
+
+ private final AnimatorUpdateListener mButtonTranslationListener = animator -> {
+ int pos = (int) animator.getAnimatedValue();
+ if (!mQuickScrubActive) {
+ pos = mDragPositive ? Math.min((int) mTranslation, pos) : Math.max((int) mTranslation, pos);
+ }
+ final View homeView = mNavigationBarView.getHomeButton().getCurrentView();
+ if (mIsVertical) {
+ homeView.setTranslationY(pos);
+ } else {
+ homeView.setTranslationX(pos);
+ }
+ };
+
+ private AnimatorListenerAdapter mQuickScrubEndListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mNavigationBarView.getHomeButton().setClickable(true);
+ mQuickScrubActive = false;
+ mTranslation = 0;
+ }
+ };
+
+ private Runnable mLongPressRunnable = this::startQuickScrub;
+
+ private final GestureDetector.SimpleOnGestureListener mGestureListener =
+ new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velX, float velY) {
+ if (mQuickScrubActive) {
+ return false;
+ }
+ float velocityX = mIsRTL ? -velX : velX;
+ float absVelY = Math.abs(velY);
+ final boolean isValidFling = velocityX > QUICK_SWITCH_FLING_VELOCITY &&
+ mIsVertical ? (absVelY > velocityX) : (velocityX > absVelY);
+ if (isValidFling) {
+ mDraggingActive = false;
+ mButtonAnimator.setIntValues((int) mTranslation, 0);
+ mButtonAnimator.start();
+ mHandler.removeCallbacks(mLongPressRunnable);
+ try {
+ final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
+ overviewProxy.onQuickSwitch();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send start of quick switch.", e);
+ }
+ }
+ return true;
+ }
+ };
+
+ public QuickScrubController(Context context) {
+ mContext = context;
+ mScrollTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ mDisplay = ((WindowManager) context.getSystemService(
+ Context.WINDOW_SERVICE)).getDefaultDisplay();
+ mOverviewEventSender = Dependency.get(OverviewProxyService.class);
+ mGestureDetector = new GestureDetector(mContext, mGestureListener);
+ mTrackThickness = getDimensionPixelSize(mContext, R.dimen.nav_quick_scrub_track_thickness);
+ mTrackPadding = getDimensionPixelSize(mContext, R.dimen.nav_quick_scrub_track_edge_padding);
+
+ mTrackAnimator = ObjectAnimator.ofFloat();
+ mTrackAnimator.addUpdateListener(mTrackAnimatorListener);
+ mButtonAnimator = ObjectAnimator.ofInt();
+ mButtonAnimator.addUpdateListener(mButtonTranslationListener);
+ mQuickScrubEndAnimator = new AnimatorSet();
+ mQuickScrubEndAnimator.playTogether(mTrackAnimator, mButtonAnimator);
+ mQuickScrubEndAnimator.setDuration(ANIM_DURATION_MS);
+ mQuickScrubEndAnimator.addListener(mQuickScrubEndListener);
+ mQuickScrubEndAnimator.setInterpolator(mQuickScrubEndInterpolator);
+ }
+
+ public void setComponents(NavigationBarView navigationBarView) {
+ mNavigationBarView = navigationBarView;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
+ final ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
+ if (overviewProxy == null) {
+ homeButton.setDelayTouchFeedback(false);
+ return false;
+ }
+ mGestureDetector.onTouchEvent(event);
+ int action = event.getAction();
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN: {
+ int x = (int) event.getX();
+ int y = (int) event.getY();
+ if (mHomeButtonRect.contains(x, y)) {
+ mTouchDownX = x;
+ mTouchDownY = y;
+ homeButton.setDelayTouchFeedback(true);
+ mHandler.postDelayed(mLongPressRunnable, LONG_PRESS_DELAY_MS);
+ } else {
+ mTouchDownX = mTouchDownY = -1;
+ }
+ break;
+ }
+ case MotionEvent.ACTION_MOVE: {
+ if (mTouchDownX != -1) {
+ int x = (int) event.getX();
+ int y = (int) event.getY();
+ int xDiff = Math.abs(x - mTouchDownX);
+ int yDiff = Math.abs(y - mTouchDownY);
+ boolean exceededTouchSlopX = xDiff > mScrollTouchSlop && xDiff > yDiff;
+ boolean exceededTouchSlopY = yDiff > mScrollTouchSlop && yDiff > xDiff;
+ boolean exceededTouchSlop, exceededPerpendicularTouchSlop;
+ int pos, touchDown, offset, trackSize;
+
+ if (mIsVertical) {
+ exceededTouchSlop = exceededTouchSlopY;
+ exceededPerpendicularTouchSlop = exceededTouchSlopX;
+ pos = y;
+ touchDown = mTouchDownY;
+ offset = pos - mTrackRect.top;
+ trackSize = mTrackRect.height();
+ } else {
+ exceededTouchSlop = exceededTouchSlopX;
+ exceededPerpendicularTouchSlop = exceededTouchSlopY;
+ pos = x;
+ touchDown = mTouchDownX;
+ offset = pos - mTrackRect.left;
+ trackSize = mTrackRect.width();
+ }
+ // Do not start scrubbing when dragging in the perpendicular direction
+ if (!mDraggingActive && exceededPerpendicularTouchSlop) {
+ mHandler.removeCallbacksAndMessages(null);
+ return false;
+ }
+ if (!mDragPositive) {
+ offset -= mIsVertical ? mTrackRect.height() : mTrackRect.width();
+ }
+
+ // Control the button movement
+ if (!mDraggingActive && exceededTouchSlop) {
+ boolean allowDrag = !mDragPositive
+ ? offset < 0 && pos < touchDown : offset >= 0 && pos > touchDown;
+ if (allowDrag) {
+ mDownOffset = offset;
+ homeButton.setClickable(false);
+ mDraggingActive = true;
+ }
+ }
+ if (mDraggingActive && (mDragPositive && offset >= 0
+ || !mDragPositive && offset <= 0)) {
+ float scrubFraction =
+ Utilities.clamp(Math.abs(offset) * 1f / trackSize, 0, 1);
+ mTranslation = !mDragPositive
+ ? Utilities.clamp(offset - mDownOffset, -trackSize, 0)
+ : Utilities.clamp(offset - mDownOffset, 0, trackSize);
+ if (mQuickScrubActive) {
+ try {
+ overviewProxy.onQuickScrubProgress(scrubFraction);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send progress of quick scrub.", e);
+ }
+ } else {
+ mTranslation /= SWITCH_STICKINESS;
+ }
+ if (mIsVertical) {
+ homeButton.getCurrentView().setTranslationY(mTranslation);
+ } else {
+ homeButton.getCurrentView().setTranslationX(mTranslation);
+ }
+ }
+ }
+ break;
+ }
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ endQuickScrub();
+ break;
+ }
+ return mDraggingActive || mQuickScrubActive;
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ canvas.drawRect(mTrackRect, mTrackPaint);
+ }
+
+ @Override
+ public void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ final int width = right - left;
+ final int height = bottom - top;
+ final int x1, x2, y1, y2;
+ if (mIsVertical) {
+ x1 = (width - mTrackThickness) / 2;
+ x2 = x1 + mTrackThickness;
+ y1 = mDragPositive ? height / 2 : mTrackPadding;
+ y2 = y1 + height / 2 - mTrackPadding;
+ } else {
+ y1 = (height - mTrackThickness) / 2;
+ y2 = y1 + mTrackThickness;
+ x1 = mDragPositive ? width / 2 : mTrackPadding;
+ x2 = x1 + width / 2 - mTrackPadding;
+ }
+ mTrackRect.set(x1, y1, x2, y2);
+
+ // Get the touch rect of the home button location
+ View homeView = mNavigationBarView.getHomeButton().getCurrentView();
+ if (homeView != null) {
+ int[] globalHomePos = homeView.getLocationOnScreen();
+ int[] globalNavBarPos = mNavigationBarView.getLocationOnScreen();
+ int homeX = globalHomePos[0] - globalNavBarPos[0];
+ int homeY = globalHomePos[1] - globalNavBarPos[1];
+ mHomeButtonRect.set(homeX, homeY, homeX + homeView.getMeasuredWidth(),
+ homeY + homeView.getMeasuredHeight());
+ }
+ }
+
+ @Override
+ public void onDarkIntensityChange(float intensity) {
+ if (intensity == 0) {
+ mTrackPaint.setColor(mContext.getColor(R.color.quick_step_track_background_light));
+ } else if (intensity == 1) {
+ mTrackPaint.setColor(mContext.getColor(R.color.quick_step_track_background_dark));
+ }
+ mMaxTrackPaintAlpha = mTrackPaint.getAlpha() * 1f / 255;
+ mTrackPaint.setAlpha(0);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ endQuickScrub();
+ }
+ return false;
+ }
+
+ @Override
+ public void setBarState(boolean isVertical, boolean isRTL) {
+ mIsVertical = isVertical;
+ mIsRTL = isRTL;
+ try {
+ int navbarPos = WindowManagerGlobal.getWindowManagerService().getNavBarPosition();
+ mDragPositive = navbarPos == NAV_BAR_LEFT || navbarPos == NAV_BAR_BOTTOM;
+ if (isRTL) {
+ mDragPositive = !mDragPositive;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get nav bar position.", e);
+ }
+ }
+
+ private void startQuickScrub() {
+ if (!mQuickScrubActive) {
+ mQuickScrubActive = true;
+ mTrackAnimator.setFloatValues(0, mMaxTrackPaintAlpha);
+ mTrackAnimator.start();
+ try {
+ mOverviewEventSender.getProxy().onQuickScrubStart();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send start of quick scrub.", e);
+ }
+ }
+ }
+
+ private void endQuickScrub() {
+ mHandler.removeCallbacks(mLongPressRunnable);
+ if (mDraggingActive || mQuickScrubActive) {
+ mButtonAnimator.setIntValues((int) mTranslation, 0);
+ mTrackAnimator.setFloatValues(mTrackPaint.getAlpha() * 1f / 255, 0);
+ mQuickScrubEndAnimator.start();
+ try {
+ mOverviewEventSender.getProxy().onQuickScrubEnd();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send end of quick scrub.", e);
+ }
+ }
+ mDraggingActive = false;
+ }
+
+ private int getDimensionPixelSize(Context context, @DimenRes int resId) {
+ return context.getResources().getDimensionPixelSize(resId);
+ }
+}
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 2da1e4d..5e08ec3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1596,7 +1596,7 @@
final boolean hasArtwork = artworkDrawable != null;
- if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
+ if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK) && !mDozing
&& (mState != StatusBarState.SHADE || allowWhenShade)
&& mFingerprintUnlockController.getMode()
!= FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
@@ -1657,15 +1657,16 @@
}
}
} else {
- // need to hide the album art, either because we are unlocked or because
- // the metadata isn't there to support it
+ // need to hide the album art, either because we are unlocked, on AOD
+ // or because the metadata isn't there to support it
if (mBackdrop.getVisibility() != View.GONE) {
if (DEBUG_MEDIA) {
Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
}
+ boolean cannotAnimateDoze = mDozing && !ScrimState.AOD.getAnimateChange();
if (mFingerprintUnlockController.getMode()
== FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
- || hideBecauseOccluded) {
+ || hideBecauseOccluded || cannotAnimateDoze) {
// We are unlocking directly - no animation!
mBackdrop.setVisibility(View.GONE);
@@ -4644,7 +4645,7 @@
@Override
public void dozeTimeTick() {
- mNotificationPanel.refreshTime();
+ mNotificationPanel.dozeTimeTick();
}
@Override
@@ -4930,18 +4931,11 @@
// system process is dead if we're here.
}
if (parentToCancelFinal != null) {
- // We have to post it to the UI thread for synchronization
- mHandler.post(() -> {
- Runnable removeRunnable =
- () -> mEntryManager.performRemoveNotification(parentToCancelFinal);
- if (isCollapsing()) {
- // To avoid lags we're only performing the remove
- // after the shade was collapsed
- addPostCollapseAction(removeRunnable);
- } else {
- removeRunnable.run();
- }
- });
+ removeNotification(parentToCancelFinal);
+ }
+ if (shouldAutoCancel(sbn)) {
+ // Automatically remove all notifications that we may have kept around longer
+ removeNotification(sbn);
}
};
@@ -4965,6 +4959,21 @@
}, afterKeyguardGone);
}
+ private void removeNotification(StatusBarNotification notification) {
+ // We have to post it to the UI thread for synchronization
+ mHandler.post(() -> {
+ Runnable removeRunnable =
+ () -> mEntryManager.performRemoveNotification(notification);
+ if (isCollapsing()) {
+ // To avoid lags we're only performing the remove
+ // after the shade was collapsed
+ addPostCollapseAction(removeRunnable);
+ } else {
+ removeRunnable.run();
+ }
+ });
+ }
+
protected NotificationListener mNotificationListener;
protected void notifyUserAboutHiddenNotifications() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 8504d8e..c667309 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -175,13 +175,18 @@
public void dismissWithAction(OnDismissAction r, Runnable cancelAction,
boolean afterKeyguardGone) {
+ dismissWithAction(r, cancelAction, afterKeyguardGone, null /* message */);
+ }
+
+ public void dismissWithAction(OnDismissAction r, Runnable cancelAction,
+ boolean afterKeyguardGone, String message) {
if (mShowing) {
cancelPendingWakeupAction();
// If we're dozing, this needs to be delayed until after we wake up - unless we're
// wake-and-unlocking, because there dozing will last until the end of the transition.
if (mDozing && !isWakeAndUnlocking()) {
mPendingWakeupAction = new DismissWithActionRequest(
- r, cancelAction, afterKeyguardGone);
+ r, cancelAction, afterKeyguardGone, message);
return;
}
@@ -632,7 +637,7 @@
if (request != null) {
if (mShowing) {
dismissWithAction(request.dismissAction, request.cancelAction,
- request.afterKeyguardGone);
+ request.afterKeyguardGone, request.message);
} else if (request.dismissAction != null) {
request.dismissAction.onDismiss();
}
@@ -651,12 +656,14 @@
final OnDismissAction dismissAction;
final Runnable cancelAction;
final boolean afterKeyguardGone;
+ final String message;
DismissWithActionRequest(OnDismissAction dismissAction, Runnable cancelAction,
- boolean afterKeyguardGone) {
+ boolean afterKeyguardGone, String message) {
this.dismissAction = dismissAction;
this.cancelAction = cancelAction;
this.afterKeyguardGone = afterKeyguardGone;
+ this.message = message;
}
}
}
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 21a96e2..cce9d1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
@@ -39,7 +39,7 @@
}
}
- private KeyButtonDrawable(Drawable[] drawables) {
+ protected KeyButtonDrawable(Drawable[] drawables) {
super(drawables);
for (int i = 0; i < drawables.length; i++) {
setLayerGravity(i, Gravity.CENTER);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
index cc7943b8..a2bec98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
@@ -26,9 +26,11 @@
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
import android.view.DisplayListCanvas;
import android.view.RenderNodeAnimator;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.animation.Interpolator;
import com.android.systemui.Interpolators;
@@ -56,14 +58,17 @@
private float mGlowAlpha = 0f;
private float mGlowScale = 1f;
private boolean mPressed;
+ private boolean mVisible;
private boolean mDrawingHardwareGlow;
private int mMaxWidth;
private boolean mLastDark;
private boolean mDark;
+ private boolean mDelayTouchFeedback;
private final Interpolator mInterpolator = new LogInterpolator();
private boolean mSupportHardware;
private final View mTargetView;
+ private final Handler mHandler = new Handler();
private final HashSet<Animator> mRunningAnimations = new HashSet<>();
private final ArrayList<Animator> mTmpArray = new ArrayList<>();
@@ -77,6 +82,10 @@
mDark = darkIntensity >= 0.5f;
}
+ public void setDelayTouchFeedback(boolean delay) {
+ mDelayTouchFeedback = delay;
+ }
+
private Paint getRipplePaint() {
if (mRipplePaint == null) {
mRipplePaint = new Paint();
@@ -211,7 +220,16 @@
}
}
+ /**
+ * Abort the ripple while it is delayed and before shown used only when setShouldDelayStartTouch
+ * is enabled.
+ */
+ public void abortDelayedRipple() {
+ mHandler.removeCallbacksAndMessages(null);
+ }
+
private void cancelAnimations() {
+ mVisible = false;
mTmpArray.addAll(mRunningAnimations);
int size = mTmpArray.size();
for (int i = 0; i < size; i++) {
@@ -220,11 +238,21 @@
}
mTmpArray.clear();
mRunningAnimations.clear();
+ mHandler.removeCallbacksAndMessages(null);
}
private void setPressedSoftware(boolean pressed) {
if (pressed) {
- enterSoftware();
+ if (mDelayTouchFeedback) {
+ if (mRunningAnimations.isEmpty()) {
+ mHandler.removeCallbacksAndMessages(null);
+ mHandler.postDelayed(this::enterSoftware, ViewConfiguration.getTapTimeout());
+ } else if (mVisible) {
+ enterSoftware();
+ }
+ } else {
+ enterSoftware();
+ }
} else {
exitSoftware();
}
@@ -232,6 +260,7 @@
private void enterSoftware() {
cancelAnimations();
+ mVisible = true;
mGlowAlpha = getMaxGlowAlpha();
ObjectAnimator scaleAnimator = ObjectAnimator.ofFloat(this, "glowScale",
0f, GLOW_MAX_SCALE_FACTOR);
@@ -240,6 +269,12 @@
scaleAnimator.addListener(mAnimatorListener);
scaleAnimator.start();
mRunningAnimations.add(scaleAnimator);
+
+ // With the delay, it could eventually animate the enter animation with no pressed state,
+ // then immediately show the exit animation. If this is skipped there will be no ripple.
+ if (mDelayTouchFeedback && !mPressed) {
+ exitSoftware();
+ }
}
private void exitSoftware() {
@@ -253,7 +288,16 @@
private void setPressedHardware(boolean pressed) {
if (pressed) {
- enterHardware();
+ if (mDelayTouchFeedback) {
+ if (mRunningAnimations.isEmpty()) {
+ mHandler.removeCallbacksAndMessages(null);
+ mHandler.postDelayed(this::enterHardware, ViewConfiguration.getTapTimeout());
+ } else if (mVisible) {
+ enterHardware();
+ }
+ } else {
+ enterHardware();
+ }
} else {
exitHardware();
}
@@ -302,6 +346,7 @@
private void enterHardware() {
cancelAnimations();
+ mVisible = true;
mDrawingHardwareGlow = true;
setExtendStart(CanvasProperty.createFloat(getExtendSize() / 2));
final RenderNodeAnimator startAnim = new RenderNodeAnimator(getExtendStart(),
@@ -343,6 +388,12 @@
mRunningAnimations.add(endAnim);
invalidateSelf();
+
+ // With the delay, it could eventually animate the enter animation with no pressed state,
+ // then immediately show the exit animation. If this is skipped there will be no ripple.
+ if (mDelayTouchFeedback && !mPressed) {
+ exitHardware();
+ }
}
private void exitHardware() {
@@ -366,6 +417,7 @@
public void onAnimationEnd(Animator animation) {
mRunningAnimations.remove(animation);
if (mRunningAnimations.isEmpty() && !mPressed) {
+ mVisible = false;
mDrawingHardwareGlow = false;
invalidateSelf();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 0501771..077c6c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -284,6 +284,7 @@
@Override
public void abortCurrentGesture() {
setPressed(false);
+ mRipple.abortDelayedRipple();
mGestureAborted = true;
}
@@ -301,6 +302,11 @@
}
@Override
+ public void setDelayTouchFeedback(boolean shouldDelay) {
+ mRipple.setDelayTouchFeedback(shouldDelay);
+ }
+
+ @Override
public void setVertical(boolean vertical) {
//no op
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 4fc5044..c377feb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -27,7 +27,9 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.os.SystemClock;
import android.text.Editable;
+import android.text.SpannedString;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
@@ -140,6 +142,7 @@
mSendButton.setVisibility(INVISIBLE);
mProgressBar.setVisibility(VISIBLE);
mEntry.remoteInputText = mEditText.getText();
+ mEntry.lastRemoteInputSent = SystemClock.elapsedRealtime();
mController.addSpinning(mEntry.key, mToken);
mController.removeRemoteInput(mEntry, mToken);
mEditText.mShowImeOnInputConnection = false;
@@ -298,6 +301,7 @@
private void reset() {
mResetting = true;
+ mEntry.remoteInputTextWhenReset = SpannedString.valueOf(mEditText.getText());
mEditText.getText().clear();
mEditText.setEnabled(true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
new file mode 100644
index 0000000..1dcdf63
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -0,0 +1,63 @@
+package com.android.systemui.statusbar.policy;
+
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+
+/** View which displays smart reply buttons in notifications. */
+public class SmartReplyView extends LinearLayout {
+
+ private static final String TAG = "SmartReplyView";
+
+ public SmartReplyView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setRepliesFromRemoteInput(RemoteInput remoteInput, PendingIntent pendingIntent) {
+ removeAllViews();
+ if (remoteInput != null && pendingIntent != null) {
+ CharSequence[] choices = remoteInput.getChoices();
+ if (choices != null) {
+ for (CharSequence choice : choices) {
+ Button replyButton = inflateReplyButton(
+ getContext(), this, choice, remoteInput, pendingIntent);
+ addView(replyButton);
+ }
+ }
+ }
+ }
+
+ public static SmartReplyView inflate(Context context, ViewGroup root) {
+ return (SmartReplyView)
+ LayoutInflater.from(context).inflate(R.layout.smart_reply_view, root, false);
+ }
+
+ private static Button inflateReplyButton(Context context, ViewGroup root, CharSequence choice,
+ RemoteInput remoteInput, PendingIntent pendingIntent) {
+ Button b = (Button) LayoutInflater.from(context).inflate(
+ R.layout.smart_reply_button, root, false);
+ b.setText(choice);
+ b.setOnClickListener(view -> {
+ Bundle results = new Bundle();
+ results.putString(remoteInput.getResultKey(), choice.toString());
+ Intent intent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ RemoteInput.addResultsToIntent(new RemoteInput[]{remoteInput}, intent, results);
+ try {
+ pendingIntent.send(context, 0, intent);
+ } catch (PendingIntent.CanceledException e) {
+ Log.w(TAG, "Unable to send smart reply", e);
+ }
+ });
+ return b;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TintedKeyButtonDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TintedKeyButtonDrawable.java
new file mode 100644
index 0000000..acf9c00
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TintedKeyButtonDrawable.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.annotation.ColorInt;
+import android.graphics.drawable.Drawable;
+
+import com.android.internal.graphics.ColorUtils;
+
+/**
+ * Drawable for {@link KeyButtonView}s which contains a single asset and colors for light and dark
+ * navigation bar mode.
+ */
+public class TintedKeyButtonDrawable extends KeyButtonDrawable {
+
+ private final int mLightColor;
+ private final int mDarkColor;
+
+ public static TintedKeyButtonDrawable create(Drawable drawable, @ColorInt int lightColor,
+ @ColorInt int darkColor) {
+ return new TintedKeyButtonDrawable(new Drawable[] { drawable }, lightColor, darkColor);
+ }
+
+ private TintedKeyButtonDrawable(Drawable[] drawables, int lightColor, int darkColor){
+ super(drawables);
+ mLightColor = lightColor;
+ mDarkColor = darkColor;
+ }
+
+ @Override
+ public void setDarkIntensity(float intensity) {
+ // Duplicate intensity scaling from KeyButtonDrawable
+ int intermediateColor = ColorUtils.compositeColors(
+ setAlphaFloat(mDarkColor, intensity),
+ setAlphaFloat(mLightColor,1f - intensity));
+ getDrawable(0).setTint(intermediateColor);
+ invalidateSelf();
+ }
+
+ private int setAlphaFloat(int color, float alpha) {
+ return ColorUtils.setAlphaComponent(color, (int) (alpha * 255f));
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index c0241e3..5505099 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -128,12 +128,11 @@
mDividerHeight = res.getDimensionPixelSize(
R.dimen.notification_children_container_divider_height);
mDividerAlpha = res.getFloat(R.dimen.notification_divider_alpha);
- mHeaderHeight = res.getDimensionPixelSize(
- R.dimen.notification_children_container_header_height);
mNotificationHeaderMargin = res.getDimensionPixelSize(
R.dimen.notification_children_container_margin_top);
mNotificatonTopPadding = res.getDimensionPixelSize(
R.dimen.notification_children_container_top_padding);
+ mHeaderHeight = mNotificationHeaderMargin + mNotificatonTopPadding;
mCollapsedBottompadding = res.getDimensionPixelSize(
com.android.internal.R.dimen.notification_content_margin_bottom);
mEnableShadowOnChildNotifications =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 369e7ff..167508a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -76,7 +76,6 @@
import com.android.systemui.statusbar.DismissView;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.ExpandableOutlineView;
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationGuts;
@@ -86,10 +85,8 @@
import com.android.systemui.statusbar.NotificationSnooze;
import com.android.systemui.statusbar.StackScrollerDecorView;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationUtils;
-import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -126,16 +123,7 @@
/**
* Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
*/
- private static final int INVALID_POINTER = -1;
- private static final AnimatableProperty SIDE_PADDINGS = AnimatableProperty.from(
- "sidePaddings",
- NotificationStackScrollLayout::setCurrentSidePadding,
- NotificationStackScrollLayout::getCurrentSidePadding,
- R.id.side_padding_animator_tag,
- R.id.side_padding_animator_end_tag,
- R.id.side_padding_animator_start_tag);
- private static final AnimationProperties SIDE_PADDING_PROPERTIES =
- new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ private static final int INVALID_POINTER = -1;;
private ExpandHelper mExpandHelper;
private NotificationSwipeHelper mSwipeHelper;
@@ -143,7 +131,6 @@
private int mCurrentStackHeight = Integer.MAX_VALUE;
private final Paint mBackgroundPaint = new Paint();
private final Path mBackgroundPath = new Path();
- private final float[] mBackgroundRadii = new float[8];
private final boolean mShouldDrawNotificationBackground;
private float mExpandedHeight;
@@ -174,7 +161,6 @@
private int mTopPadding;
private int mBottomMargin;
private int mBottomInset = 0;
- private float mCurrentSidePadding;
/**
* The algorithm which calculates the properties for our children
@@ -402,7 +388,6 @@
private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
private Runnable mAnimateScroll = this::animateScroll;
private int mCornerRadius;
- private int mLockscreenSidePaddings;
private int mSidePaddings;
public NotificationStackScrollLayout(Context context) {
@@ -440,8 +425,7 @@
res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
updateWillNotDraw();
- mBackgroundPaint.setAntiAlias(true);
- mBackgroundPaint.setStyle(Paint.Style.FILL);
+ mBackgroundPaint.setAntiAlias(true);;
if (DEBUG) {
mDebugPaint = new Paint();
mDebugPaint.setColor(0xffff0000);
@@ -490,7 +474,8 @@
protected void onDraw(Canvas canvas) {
if (mShouldDrawNotificationBackground && !mAmbientState.isDark()
&& mCurrentBounds.top < mCurrentBounds.bottom) {
- canvas.drawPath(mBackgroundPath, mBackgroundPaint);
+ canvas.drawRoundRect(mSidePaddings, mCurrentBounds.top, getWidth() - mSidePaddings,
+ mCurrentBounds.bottom, mCornerRadius, mCornerRadius, mBackgroundPaint);
}
if (DEBUG) {
@@ -543,8 +528,7 @@
R.dimen.min_top_overscroll_to_qs);
mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height);
mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
- mLockscreenSidePaddings = res.getDimensionPixelSize(
- R.dimen.notification_lockscreen_side_paddings);
+ mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
mMinInteractionHeight = res.getDimensionPixelSize(
R.dimen.notification_min_interaction_height);
mCornerRadius = res.getDimensionPixelSize(
@@ -575,11 +559,15 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ int childWidthSpec = MeasureSpec.makeMeasureSpec(width - mSidePaddings * 2,
+ MeasureSpec.getMode(widthMeasureSpec));
// We need to measure all children even the GONE ones, such that the heights are calculated
// correctly as they are used to calculate how many we can fit on the screen.
final int size = getChildCount();
for (int i = 0; i < size; i++) {
- measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
+ measureChild(getChildAt(i), childWidthSpec, heightMeasureSpec);
}
}
@@ -675,11 +663,32 @@
private void onPreDrawDuringAnimation() {
mShelf.updateAppearance();
+ updateClippingToTopRoundedCorner();
if (!mNeedsAnimation && !mChildrenUpdateRequested) {
updateBackground();
}
}
+ private void updateClippingToTopRoundedCorner() {
+ Float clipStart = (float) mTopPadding;
+ Float clipEnd = clipStart + mCornerRadius;
+ boolean first = true;
+ for (int i = 0; i < getChildCount(); i++) {
+ ExpandableView child = (ExpandableView) getChildAt(i);
+ if (child.getVisibility() == GONE) {
+ continue;
+ }
+ float start = child.getTranslationY();
+ float end = start + Math.max(child.getActualHeight() - child.getClipBottomAmount(),
+ 0);
+ boolean clip = clipStart > start && clipStart < end
+ || clipEnd >= start && clipEnd <= end;
+ clip &= !(first && mOwnScrollY == 0);
+ child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0) : -1);
+ first = false;
+ }
+ }
+
private void updateScrollStateForAddedChildren() {
if (mChildrenToAddAnimated.isEmpty()) {
return;
@@ -2255,31 +2264,9 @@
mScrimController.setExcludedBackgroundArea(
mFadingOut || mParentNotFullyVisible || mAmbientState.isDark() || mIsClipped ? null
: mCurrentBounds);
- updateBackgroundPath();
invalidate();
}
- private void updateBackgroundPath() {
- mBackgroundPath.reset();
- float topRoundness = 0;
- if (mFirstVisibleBackgroundChild != null) {
- topRoundness = mFirstVisibleBackgroundChild.getCurrentBackgroundRadiusTop();
- }
- topRoundness = onKeyguard() ? mCornerRadius : topRoundness;
- float bottomRoundNess = mCornerRadius;
- mBackgroundRadii[0] = topRoundness;
- mBackgroundRadii[1] = topRoundness;
- mBackgroundRadii[2] = topRoundness;
- mBackgroundRadii[3] = topRoundness;
- mBackgroundRadii[4] = bottomRoundNess;
- mBackgroundRadii[5] = bottomRoundNess;
- mBackgroundRadii[6] = bottomRoundNess;
- mBackgroundRadii[7] = bottomRoundNess;
- mBackgroundPath.addRoundRect(mCurrentSidePadding, mCurrentBounds.top,
- getWidth() - mCurrentSidePadding, mCurrentBounds.bottom, mBackgroundRadii,
- Path.Direction.CCW);
- }
-
/**
* Update the background bounds to the new desired bounds
*/
@@ -2292,8 +2279,8 @@
mBackgroundBounds.left = mTempInt2[0];
mBackgroundBounds.right = mTempInt2[0] + getWidth();
}
- mBackgroundBounds.left += mCurrentSidePadding;
- mBackgroundBounds.right -= mCurrentSidePadding;
+ mBackgroundBounds.left += mSidePaddings;
+ mBackgroundBounds.right -= mSidePaddings;
if (!mIsExpanded) {
mBackgroundBounds.top = 0;
mBackgroundBounds.bottom = 0;
@@ -2902,8 +2889,7 @@
private void applyRoundedNess() {
if (mFirstVisibleBackgroundChild != null) {
- mFirstVisibleBackgroundChild.setTopRoundness(
- mStatusBarState == StatusBarState.KEYGUARD ? 1.0f : 0.0f,
+ mFirstVisibleBackgroundChild.setTopRoundness(1.0f,
mFirstVisibleBackgroundChild.isShown()
&& !mChildrenToAddAnimated.contains(mFirstVisibleBackgroundChild));
}
@@ -2912,7 +2898,6 @@
mLastVisibleBackgroundChild.isShown()
&& !mChildrenToAddAnimated.contains(mLastVisibleBackgroundChild));
}
- updateBackgroundPath();
invalidate();
}
@@ -2922,7 +2907,6 @@
generateAddAnimation(child, false /* fromMoreCard */);
updateAnimationState(child);
updateChronometerForChild(child);
- updateCurrentSidePaddings(child);
}
private void updateHideSensitiveForChild(View child) {
@@ -3021,6 +3005,7 @@
mAnimationEvents.clear();
updateBackground();
updateViewShadows();
+ updateClippingToTopRoundedCorner();
} else {
applyCurrentState();
}
@@ -3714,6 +3699,7 @@
setAnimationRunning(false);
updateBackground();
updateViewShadows();
+ updateClippingToTopRoundedCorner();
}
private void updateViewShadows() {
@@ -4382,43 +4368,6 @@
public void setStatusBarState(int statusBarState) {
mStatusBarState = statusBarState;
mAmbientState.setStatusBarState(statusBarState);
- applyRoundedNess();
- updateSidePaddings();
- }
-
- private void updateSidePaddings() {
- int sidePaddings = mStatusBarState == StatusBarState.KEYGUARD ? mLockscreenSidePaddings : 0;
- if (sidePaddings != mSidePaddings) {
- boolean animate = isShown();
- mSidePaddings = sidePaddings;
- PropertyAnimator.setProperty(this, SIDE_PADDINGS, sidePaddings,
- SIDE_PADDING_PROPERTIES, animate);
- }
- }
-
- protected void setCurrentSidePadding(float sidePadding) {
- mCurrentSidePadding = sidePadding;
- updateBackground();
- applySidePaddingsToChildren();
- }
-
- private void applySidePaddingsToChildren() {
- for (int i = 0; i < getChildCount(); i++) {
- View view = getChildAt(i);
- updateCurrentSidePaddings(view);
- }
- }
-
- private void updateCurrentSidePaddings(View view) {
- if (!(view instanceof ExpandableOutlineView)) {
- return;
- }
- ExpandableOutlineView outlineView = (ExpandableOutlineView) view;
- outlineView.setCurrentSidePaddings(mCurrentSidePadding);
- }
-
- protected float getCurrentSidePadding() {
- return mCurrentSidePadding;
}
public void setExpandingVelocity(float expandingVelocity) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index bc98140f..efa8386 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -52,7 +52,7 @@
public static final String VOLUME_UP_SILENT = "sysui_volume_up_silent";
public static final String VOLUME_SILENT_DO_NOT_DISTURB = "sysui_do_not_disturb";
- public static final boolean DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT = true;
+ public static final boolean DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT = false;
public static final boolean DEFAULT_VOLUME_UP_TO_EXIT_SILENT = true;
public static final boolean DEFAULT_DO_NOT_DISTURB_WHEN_SILENT = true;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 7b91f14..5a19a76 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -20,6 +20,7 @@
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC;
import static com.android.systemui.volume.Events.DISMISS_REASON_OUTPUT_CHOOSER;
+import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED;
import static com.android.systemui.volume.Events.DISMISS_REASON_TOUCH_OUTSIDE;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -30,14 +31,13 @@
import android.app.KeyguardManager;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
-import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.os.Debug;
@@ -45,9 +45,8 @@
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
+import android.provider.Settings;
import android.provider.Settings.Global;
-import android.transition.AutoTransition;
-import android.transition.TransitionManager;
import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -72,7 +71,6 @@
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.plugins.VolumeDialog;
import com.android.systemui.plugins.VolumeDialogController;
@@ -104,7 +102,6 @@
private CustomDialog mDialog;
private ViewGroup mDialogView;
private ViewGroup mDialogRowsView;
- private ImageButton mExpandButton;
private ImageButton mRingerIcon;
private ImageButton mOutputChooser;
private TextView mRingerStatus;
@@ -120,8 +117,6 @@
private final ColorStateList mInactiveSliderTint;
private boolean mShowing;
- private boolean mExpanded;
- private boolean mExpandButtonAnimationRunning;
private boolean mShowA11yStream;
private int mActiveStream;
@@ -182,11 +177,11 @@
mDialog.setContentView(R.layout.volume_dialog);
mDialog.setOnShowListener(dialog -> {
- mDialogView.setTranslationY(-mDialogView.getHeight());
+ mDialogView.setTranslationX(mDialogView.getWidth() / 2);
mDialogView.setAlpha(0);
mDialogView.animate()
.alpha(1)
- .translationY(0)
+ .translationX(0)
.setDuration(300)
.setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
.withEndAction(() -> {
@@ -205,20 +200,10 @@
VolumeUiLayout hardwareLayout = VolumeUiLayout.get(mDialogView);
hardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE));
- ViewGroup dialogContentView = mDialog.findViewById(R.id.volume_dialog_content);
- mDialogRowsView = dialogContentView.findViewById(R.id.volume_dialog_rows);
+ mDialogRowsView = mDialog.findViewById(R.id.volume_dialog_rows);
mRingerIcon = mDialog.findViewById(R.id.ringer_icon);
mRingerStatus = mDialog.findViewById(R.id.ringer_status);
- mExpanded = false;
- mExpandButton = mDialogView.findViewById(R.id.volume_expand_button);
- mExpandButton.setOnClickListener(mClickExpand);
- mExpandButton.setVisibility(
- AudioSystem.isSingleVolume(mContext) ? View.GONE : View.VISIBLE);
-
- mOutputChooser = mDialogView.findViewById(R.id.output_chooser);
- mOutputChooser.setOnClickListener(mClickOutputChooser);
-
if (mRows.isEmpty()) {
addRow(AudioManager.STREAM_MUSIC,
R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, true);
@@ -239,6 +224,10 @@
} else {
addExistingRows();
}
+
+ mOutputChooser = mDialogView.findViewById(R.id.output_chooser);
+ mOutputChooser.setOnClickListener(mClickOutputChooser);
+
updateRowsH(getActiveRow());
initRingerH();
}
@@ -273,11 +262,9 @@
VolumeRow row = new VolumeRow();
initRow(row, stream, iconRes, iconMuteRes, important, defaultStream);
int rowSize;
- int viewSize;
- if (mShowA11yStream && dynamic && (rowSize = mRows.size()) > 1
- && (viewSize = mDialogRowsView.getChildCount()) > 1) {
- // A11y Stream should be the last in the list
- mDialogRowsView.addView(row.view, viewSize - 2);
+ if (mShowA11yStream && dynamic && (rowSize = mRows.size()) > 1) {
+ // A11y Stream should be the first in the list, so it's shown to start of other rows
+ mDialogRowsView.addView(row.view, 0);
mRows.add(rowSize - 2, row);
} else {
mDialogRowsView.addView(row.view);
@@ -315,7 +302,6 @@
public void dump(PrintWriter writer) {
writer.println(VolumeDialogImpl.class.getSimpleName() + " state:");
writer.print(" mShowing: "); writer.println(mShowing);
- writer.print(" mExpanded: "); writer.println(mExpanded);
writer.print(" mActiveStream: "); writer.println(mActiveStream);
writer.print(" mDynamic: "); writer.println(mDynamic);
writer.print(" mAutomute: "); writer.println(mAutomute);
@@ -432,6 +418,13 @@
}
updateRingerH();
});
+ mRingerIcon.setOnLongClickListener(v -> {
+ Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ dismissH(DISMISS_REASON_SETTINGS_CLICKED);
+ mContext.startActivity(intent);
+ return true;
+ });
updateRingerH();
}
@@ -468,7 +461,6 @@
private int computeTimeoutH() {
if (mAccessibility.mFeedbackEnabled) return 20000;
if (mHovering) return 16000;
- if (mExpanded) return 5000;
if (mSafetyWarning != null) return 5000;
return 3000;
}
@@ -480,13 +472,11 @@
mDialogView.animate().cancel();
mShowing = false;
- updateExpandedH(false /* expanding */, true /* dismissing */);
-
- mDialogView.setTranslationY(0);
+ mDialogView.setTranslationX(0);
mDialogView.setAlpha(1);
mDialogView.animate()
.alpha(0)
- .translationY(-mDialogView.getHeight())
+ .translationX(mDialogView.getWidth() / 2)
.setDuration(250)
.setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
.withEndAction(() -> mHandler.postDelayed(() -> {
@@ -514,67 +504,6 @@
}
}
- private void updateExpandedH(final boolean expanded, final boolean dismissing) {
- if (D.BUG) Log.d(TAG, "updateExpandedH " + expanded);
-
- if (mExpanded == expanded) return;
- mExpanded = expanded;
- mExpandButtonAnimationRunning = isAttached();
- updateExpandButtonH();
- TransitionManager.endTransitions(mDialogView);
- final VolumeRow activeRow = getActiveRow();
- if (!dismissing) {
- mWindow.setLayout(mWindow.getAttributes().width, ViewGroup.LayoutParams.MATCH_PARENT);
- TransitionManager.beginDelayedTransition(mDialogView, getTransition());
- }
- updateRowsH(activeRow);
- rescheduleTimeoutH();
- }
-
- private AutoTransition getTransition() {
- AutoTransition transition = new AutoTransition();
- transition.setDuration(300);
- transition.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- return transition;
- }
-
- private void updateExpandButtonH() {
- if (D.BUG) Log.d(TAG, "updateExpandButtonH");
-
- mExpandButton.setClickable(!mExpandButtonAnimationRunning);
- if (!(mExpandButtonAnimationRunning && isAttached())) {
- final int res = mExpanded ? R.drawable.ic_volume_collapse_animation
- : R.drawable.ic_volume_expand_animation;
- if (hasTouchFeature()) {
- mExpandButton.setImageResource(res);
- } else {
- // if there is no touch feature, show the volume ringer instead
- mExpandButton.setImageResource(R.drawable.ic_volume_ringer);
- mExpandButton.setBackgroundResource(0); // remove gray background emphasis
- }
- mExpandButton.setContentDescription(mContext.getString(mExpanded ?
- R.string.accessibility_volume_collapse : R.string.accessibility_volume_expand));
- }
- if (mExpandButtonAnimationRunning) {
- final Drawable d = mExpandButton.getDrawable();
- if (d instanceof AnimatedVectorDrawable) {
- // workaround to reset drawable
- final AnimatedVectorDrawable avd = (AnimatedVectorDrawable) d.getConstantState()
- .newDrawable();
- mExpandButton.setImageDrawable(avd);
- avd.start();
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- mExpandButtonAnimationRunning = false;
- updateExpandButtonH();
- rescheduleTimeoutH();
- }
- }, 300);
- }
- }
- }
-
private boolean isAttached() {
return mDialogView != null && mDialogView.isAttachedToWindow();
}
@@ -597,7 +526,7 @@
return true;
}
- return row.defaultStream || isActive || (mExpanded && row.important);
+ return row.defaultStream || isActive;
}
private void updateRowsH(final VolumeRow activeRow) {
@@ -954,16 +883,6 @@
}
}
- private final OnClickListener mClickExpand = new OnClickListener() {
- @Override
- public void onClick(View v) {
- mExpandButton.animate().cancel();
- final boolean newExpand = !mExpanded;
- Events.writeEvent(mContext, Events.EVENT_EXPAND, newExpand);
- updateExpandedH(newExpand, false /* dismissing */);
- }
- };
-
private final OnClickListener mClickOutputChooser = new OnClickListener() {
@Override
public void onClick(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
index 49ac9b6..1c9cbc1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
@@ -14,15 +14,37 @@
package com.android.systemui.volume;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.content.Context;
+import android.content.res.Configuration;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+
+import com.android.systemui.R;
+import com.android.systemui.util.leak.RotationUtils;
public class VolumeUiLayout extends FrameLayout {
+ private View mChild;
+ private int mOldHeight;
+ private boolean mAnimating;
+ private AnimatorSet mAnimation;
+ private boolean mHasOutsideTouch;
+ private int mRotation = ROTATION_NONE;
public VolumeUiLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -40,11 +62,245 @@
}
@Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (mChild == null) {
+ if (getChildCount() != 0) {
+ mChild = getChildAt(0);
+ mOldHeight = mChild.getMeasuredHeight();
+ updateRotation();
+ } else {
+ return;
+ }
+ }
+ int newHeight = mChild.getMeasuredHeight();
+ if (newHeight != mOldHeight) {
+ animateChild(mOldHeight, newHeight);
+ }
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ updateRotation();
+ }
+
+ private void updateRotation() {
+ int rotation = RotationUtils.getRotation(getContext());
+ if (rotation != mRotation) {
+ rotate(mRotation, rotation);
+ mRotation = rotation;
+ }
+ }
+
+ private void rotate(View view, int from, int to, boolean swapDimens) {
+ if (from != ROTATION_NONE && to != ROTATION_NONE) {
+ // Rather than handling this confusing case, just do 2 rotations.
+ rotate(view, from, ROTATION_NONE, swapDimens);
+ rotate(view, ROTATION_NONE, to, swapDimens);
+ return;
+ }
+ if (from == ROTATION_LANDSCAPE || to == ROTATION_SEASCAPE) {
+ rotateRight(view);
+ } else {
+ rotateLeft(view);
+ }
+ if (to != ROTATION_NONE) {
+ if (swapDimens && view instanceof LinearLayout) {
+ LinearLayout linearLayout = (LinearLayout) view;
+ linearLayout.setOrientation(LinearLayout.HORIZONTAL);
+ swapDimens(view);
+ }
+ } else {
+ if (swapDimens && view instanceof LinearLayout) {
+ LinearLayout linearLayout = (LinearLayout) view;
+ linearLayout.setOrientation(LinearLayout.VERTICAL);
+ swapDimens(view);
+ }
+ }
+ }
+
+ private void rotate(int from, int to) {
+ View footer = mChild.findViewById(R.id.footer);
+ rotate(footer, from, to, false);
+ rotate(this, from, to, true);
+ rotate(mChild, from, to, true);
+ ViewGroup rows = mChild.findViewById(R.id.volume_dialog_rows);
+ rotate(rows, from, to, true);
+ int rowCount = rows.getChildCount();
+ for (int i = 0; i < rowCount; i++) {
+ View child = rows.getChildAt(i);
+ if (to == ROTATION_SEASCAPE) {
+ rotateSeekBars(to, 0);
+ } else if (to == ROTATION_LANDSCAPE) {
+ rotateSeekBars(to, 180);
+ } else {
+ rotateSeekBars(to, 270);
+ }
+ rotate(child, from, to, true);
+ }
+ }
+
+ private void swapDimens(View v) {
+ if (v == null) {
+ return;
+ }
+ ViewGroup.LayoutParams params = v.getLayoutParams();
+ int h = params.width;
+ params.width = params.height;
+ params.height = h;
+ v.setLayoutParams(params);
+ }
+
+ private void rotateSeekBars(int to, int rotation) {
+ SeekBar seekbar = mChild.findViewById(R.id.volume_row_slider);
+ if (seekbar != null) {
+ seekbar.setRotation((float) rotation);
+ }
+
+ View parent = mChild.findViewById(R.id.volume_row_slider_frame);
+ swapDimens(parent);
+ ViewGroup.LayoutParams params = seekbar.getLayoutParams();
+ ViewGroup.LayoutParams parentParams = parent.getLayoutParams();
+ if (to != ROTATION_NONE) {
+ params.height = parentParams.height;
+ params.width = parentParams.width;
+ } else {
+ params.height = parentParams.width;
+ params.width = parentParams.height;
+ }
+ seekbar.setLayoutParams(params);
+ }
+
+ private int rotateGravityRight(int gravity) {
+ int retGravity = 0;
+ int layoutDirection = getLayoutDirection();
+ final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
+ final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
+
+ switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.CENTER_HORIZONTAL:
+ retGravity |= Gravity.CENTER_VERTICAL;
+ break;
+ case Gravity.RIGHT:
+ retGravity |= Gravity.BOTTOM;
+ break;
+ case Gravity.LEFT:
+ default:
+ retGravity |= Gravity.TOP;
+ break;
+ }
+
+ switch (verticalGravity) {
+ case Gravity.CENTER_VERTICAL:
+ retGravity |= Gravity.CENTER_HORIZONTAL;
+ break;
+ case Gravity.BOTTOM:
+ retGravity |= Gravity.LEFT;
+ break;
+ case Gravity.TOP:
+ default:
+ retGravity |= Gravity.RIGHT;
+ break;
+ }
+ return retGravity;
+ }
+
+ private int rotateGravityLeft(int gravity) {
+ if (gravity == -1) {
+ gravity = Gravity.TOP | Gravity.START;
+ }
+ int retGravity = 0;
+ int layoutDirection = getLayoutDirection();
+ final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
+ final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
+
+ switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.CENTER_HORIZONTAL:
+ retGravity |= Gravity.CENTER_VERTICAL;
+ break;
+ case Gravity.RIGHT:
+ retGravity |= Gravity.TOP;
+ break;
+ case Gravity.LEFT:
+ default:
+ retGravity |= Gravity.BOTTOM;
+ break;
+ }
+
+ switch (verticalGravity) {
+ case Gravity.CENTER_VERTICAL:
+ retGravity |= Gravity.CENTER_HORIZONTAL;
+ break;
+ case Gravity.BOTTOM:
+ retGravity |= Gravity.RIGHT;
+ break;
+ case Gravity.TOP:
+ default:
+ retGravity |= Gravity.LEFT;
+ break;
+ }
+ return retGravity;
+ }
+
+ private void rotateLeft(View v) {
+ if (v.getParent() instanceof FrameLayout) {
+ LayoutParams p = (LayoutParams) v.getLayoutParams();
+ p.gravity = rotateGravityLeft(p.gravity);
+ }
+
+ v.setPadding(v.getPaddingTop(), v.getPaddingRight(), v.getPaddingBottom(),
+ v.getPaddingLeft());
+ MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams();
+ params.setMargins(params.topMargin, params.rightMargin, params.bottomMargin,
+ params.leftMargin);
+ v.setLayoutParams(params);
+ }
+
+ private void rotateRight(View v) {
+ if (v.getParent() instanceof FrameLayout) {
+ LayoutParams p = (LayoutParams) v.getLayoutParams();
+ p.gravity = rotateGravityRight(p.gravity);
+ }
+
+ v.setPadding(v.getPaddingBottom(), v.getPaddingLeft(), v.getPaddingTop(),
+ v.getPaddingRight());
+ MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams();
+ params.setMargins(params.bottomMargin, params.leftMargin, params.topMargin,
+ params.rightMargin);
+ v.setLayoutParams(params);
+ }
+
+ private void animateChild(int oldHeight, int newHeight) {
+ if (true) return;
+ if (mAnimating) {
+ mAnimation.cancel();
+ }
+ mAnimating = true;
+ mAnimation = new AnimatorSet();
+ mAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimating = false;
+ }
+ });
+ int fromTop = mChild.getTop();
+ int fromBottom = mChild.getBottom();
+ int toTop = fromTop - ((newHeight - oldHeight) / 2);
+ int toBottom = fromBottom + ((newHeight - oldHeight) / 2);
+ ObjectAnimator top = ObjectAnimator.ofInt(mChild, "top", fromTop, toTop);
+ mAnimation.playTogether(top,
+ ObjectAnimator.ofInt(mChild, "bottom", fromBottom, toBottom));
+ }
+
+
+ @Override
public ViewOutlineProvider getOutlineProvider() {
return super.getOutlineProvider();
}
public void setOutsideTouchListener(OnClickListener onClickListener) {
+ mHasOutsideTouch = true;
requestLayout();
setOnClickListener(onClickListener);
setClickable(true);
@@ -60,7 +316,14 @@
}
private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener = inoutInfo -> {
+ if (mHasOutsideTouch || (mChild == null)) {
+ inoutInfo.setTouchableInsets(
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+ return;
+ }
inoutInfo.setTouchableInsets(
- ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT);
+ inoutInfo.contentInsets.set(mChild.getLeft(), mChild.getTop(),
+ 0, getBottom() - mChild.getBottom());
};
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index be28569..cd409d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -33,8 +33,13 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
+
import androidx.app.slice.SliceItem;
+import androidx.app.slice.SliceProvider;
+import androidx.app.slice.SliceSpecs;
import androidx.app.slice.core.SliceQuery;
+import androidx.app.slice.widget.SliceLiveData;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -47,6 +52,7 @@
public void setup() {
mProvider = new TestableKeyguardSliceProvider();
mProvider.attachInfo(getContext(), null);
+ SliceProvider.setSpecs(Arrays.asList(SliceSpecs.LIST));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
index 7f07e0c..3e37cfe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
@@ -38,6 +38,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.util.NotificationChannels;
+import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,6 +47,9 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class PowerNotificationWarningsTest extends SysuiTestCase {
+
+ public static final String FORMATTED_45M = "0h 45m";
+ public static final String FORMATTED_HOUR = "1h 0m";
private final NotificationManager mMockNotificationManager = mock(NotificationManager.class);
private PowerNotificationWarnings mPowerNotificationWarnings;
@@ -147,4 +151,22 @@
verify(mMockNotificationManager, times(1)).cancelAsUser(anyString(),
eq(SystemMessage.NOTE_THERMAL_SHUTDOWN), any());
}
+
+ @Test
+ public void testGetTimeRemainingFormatted_roundsDownTo15() {
+ mPowerNotificationWarnings.updateEstimate(
+ new Estimate(TimeUnit.MINUTES.toMillis(57), true));
+ String time = mPowerNotificationWarnings.getTimeRemainingFormatted();
+
+ assertTrue("time:" + time + ", expected: " + FORMATTED_45M, time.equals(FORMATTED_45M));
+ }
+
+ @Test
+ public void testGetTimeRemainingFormatted_keepsMinutesWhenZero() {
+ mPowerNotificationWarnings.updateEstimate(
+ new Estimate(TimeUnit.MINUTES.toMillis(65), true));
+ String time = mPowerNotificationWarnings.getTimeRemainingFormatted();
+
+ assertTrue("time:" + time + ", expected: " + FORMATTED_HOUR, time.equals(FORMATTED_HOUR));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index e4734a4..fdb7f8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -19,12 +19,15 @@
import static android.os.HardwarePropertiesManager.TEMPERATURE_SHUTDOWN;
import static android.provider.Settings.Global.SHOW_TEMPERATURE_WARNING;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.os.BatteryManager;
import android.os.HardwarePropertiesManager;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
@@ -37,6 +40,7 @@
import com.android.systemui.power.PowerUI.WarningsUI;
import com.android.systemui.statusbar.phone.StatusBar;
+import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,14 +50,23 @@
@SmallTest
public class PowerUITest extends SysuiTestCase {
+ private static final boolean UNPLUGGED = false;
+ private static final boolean POWER_SAVER_OFF = false;
+ private static final int ABOVE_WARNING_BUCKET = 1;
+ public static final int BELOW_WARNING_BUCKET = -1;
+ public static final long BELOW_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(2);
+ public static final long ABOVE_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(4);
private HardwarePropertiesManager mHardProps;
private WarningsUI mMockWarnings;
private PowerUI mPowerUI;
+ private EnhancedEstimates mEnhacedEstimates;
@Before
public void setup() {
mMockWarnings = mDependency.injectMockDependency(WarningsUI.class);
+ mEnhacedEstimates = mDependency.injectMockDependency(EnhancedEstimates.class);
mHardProps = mock(HardwarePropertiesManager.class);
+
mContext.putComponent(StatusBar.class, mock(StatusBar.class));
mContext.addMockSystemService(Context.HARDWARE_PROPERTIES_SERVICE, mHardProps);
@@ -128,6 +141,180 @@
verify(mMockWarnings).showHighTemperatureWarning();
}
+ @Test
+ public void testShouldShowLowBatteryWarning_showHybridOnly_returnsShow() {
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ mPowerUI.start();
+
+ // unplugged device that would not show the non-hybrid notification but would show the
+ // hybrid
+ boolean shouldShow =
+ mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+ ABOVE_WARNING_BUCKET, Long.MAX_VALUE, BELOW_HYBRID_THRESHOLD,
+ POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
+ assertTrue(shouldShow);
+ }
+
+ @Test
+ public void testShouldShowLowBatteryWarning_showHybrid_showStandard_returnsShow() {
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ mPowerUI.start();
+
+ // unplugged device that would show the non-hybrid notification and the hybrid
+ boolean shouldShow =
+ mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+ BELOW_WARNING_BUCKET, Long.MAX_VALUE, BELOW_HYBRID_THRESHOLD,
+ POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
+ assertTrue(shouldShow);
+ }
+
+ @Test
+ public void testShouldShowLowBatteryWarning_showStandardOnly_returnsShow() {
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ mPowerUI.start();
+
+ // unplugged device that would show the non-hybrid but not the hybrid
+ boolean shouldShow =
+ mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+ BELOW_WARNING_BUCKET, Long.MAX_VALUE, ABOVE_HYBRID_THRESHOLD,
+ POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
+ assertTrue(shouldShow);
+ }
+
+ @Test
+ public void testShouldShowLowBatteryWarning_deviceHighBattery_returnsNoShow() {
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ mPowerUI.start();
+
+ // unplugged device that would show the neither due to battery level being good
+ boolean shouldShow =
+ mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+ ABOVE_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, ABOVE_HYBRID_THRESHOLD,
+ POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
+ assertFalse(shouldShow);
+ }
+
+ @Test
+ public void testShouldShowLowBatteryWarning_devicePlugged_returnsNoShow() {
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ mPowerUI.start();
+
+ // plugged device that would show the neither due to being plugged
+ boolean shouldShow =
+ mPowerUI.shouldShowLowBatteryWarning(!UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+ BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, BELOW_HYBRID_THRESHOLD,
+ POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
+ assertFalse(shouldShow);
+ }
+
+ @Test
+ public void testShouldShowLowBatteryWarning_deviceBatteryStatusUnkown_returnsNoShow() {
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ mPowerUI.start();
+
+ // Unknown battery status device that would show the neither due
+ boolean shouldShow =
+ mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+ BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, BELOW_HYBRID_THRESHOLD,
+ !POWER_SAVER_OFF, BatteryManager.BATTERY_STATUS_UNKNOWN);
+ assertFalse(shouldShow);
+ }
+
+ @Test
+ public void testShouldShowLowBatteryWarning_batterySaverEnabled_returnsNoShow() {
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ mPowerUI.start();
+
+ // BatterySaverEnabled device that would show the neither due to battery saver
+ boolean shouldShow =
+ mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+ BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, BELOW_HYBRID_THRESHOLD,
+ !POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
+ assertFalse(shouldShow);
+ }
+
+ @Test
+ public void testShouldDismissLowBatteryWarning_dismissWhenPowerSaverEnabled() {
+ mPowerUI.start();
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ // device that gets power saver turned on should dismiss
+ boolean shouldDismiss =
+ mPowerUI.shouldDismissLowBatteryWarning(UNPLUGGED, BELOW_WARNING_BUCKET,
+ BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, !POWER_SAVER_OFF);
+ assertTrue(shouldDismiss);
+ }
+
+ @Test
+ public void testShouldDismissLowBatteryWarning_dismissWhenPlugged() {
+ mPowerUI.start();
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+
+ // device that gets plugged in should dismiss
+ boolean shouldDismiss =
+ mPowerUI.shouldDismissLowBatteryWarning(!UNPLUGGED, BELOW_WARNING_BUCKET,
+ BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, POWER_SAVER_OFF);
+ assertTrue(shouldDismiss);
+ }
+
+ @Test
+ public void testShouldDismissLowBatteryWarning_dismissHybridSignal_showStandardSignal_shouldShow() {
+ mPowerUI.start();
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ // would dismiss hybrid but not non-hybrid should not dismiss
+ boolean shouldDismiss =
+ mPowerUI.shouldDismissLowBatteryWarning(UNPLUGGED, BELOW_WARNING_BUCKET,
+ BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, POWER_SAVER_OFF);
+ assertFalse(shouldDismiss);
+ }
+
+ @Test
+ public void testShouldDismissLowBatteryWarning_showHybridSignal_dismissStandardSignal_shouldShow() {
+ mPowerUI.start();
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+
+ // would dismiss non-hybrid but not hybrid should not dismiss
+ boolean shouldDismiss =
+ mPowerUI.shouldDismissLowBatteryWarning(UNPLUGGED, BELOW_WARNING_BUCKET,
+ ABOVE_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD, POWER_SAVER_OFF);
+ assertFalse(shouldDismiss);
+ }
+
+ @Test
+ public void testShouldDismissLowBatteryWarning_showBothSignal_shouldShow() {
+ mPowerUI.start();
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+
+ // should not dismiss when both would not dismiss
+ boolean shouldDismiss =
+ mPowerUI.shouldDismissLowBatteryWarning(UNPLUGGED, BELOW_WARNING_BUCKET,
+ BELOW_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD, POWER_SAVER_OFF);
+ assertFalse(shouldDismiss);
+ }
+
+ @Test
+ public void testShouldDismissLowBatteryWarning_dismissBothSignal_shouldDismiss() {
+ mPowerUI.start();
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+
+ //should dismiss if both would dismiss
+ boolean shouldDismiss =
+ mPowerUI.shouldDismissLowBatteryWarning(UNPLUGGED, BELOW_WARNING_BUCKET,
+ ABOVE_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, POWER_SAVER_OFF);
+ assertTrue(shouldDismiss);
+ }
+
+ @Test
+ public void testShouldDismissLowBatteryWarning_dismissStandardSignal_hybridDisabled_shouldDismiss() {
+ mPowerUI.start();
+ when(mEnhacedEstimates.isHybridNotificationEnabled()).thenReturn(false);
+
+ // would dismiss non-hybrid with hybrid disabled should dismiss
+ boolean shouldDismiss =
+ mPowerUI.shouldDismissLowBatteryWarning(UNPLUGGED, BELOW_WARNING_BUCKET,
+ ABOVE_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, POWER_SAVER_OFF);
+ assertTrue(shouldDismiss);
+ }
+
private void setCurrentTemp(float temp) {
when(mHardProps.getDeviceTemperatures(DEVICE_TEMPERATURE_SKIN, TEMPERATURE_CURRENT))
.thenReturn(new float[] { temp });
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
index 0a68389..f9ec3f92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
@@ -32,11 +32,14 @@
import android.app.ActivityManager;
import android.app.Notification;
+import android.app.NotificationManager;
import android.content.Context;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationRankingUpdate;
import android.service.notification.StatusBarNotification;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -120,6 +123,23 @@
}
}
+ private void setUserSentiment(String key, int sentiment) {
+ doAnswer(invocationOnMock -> {
+ NotificationListenerService.Ranking ranking = (NotificationListenerService.Ranking)
+ invocationOnMock.getArguments()[1];
+ ranking.populate(
+ key,
+ 0,
+ false,
+ 0,
+ 0,
+ NotificationManager.IMPORTANCE_DEFAULT,
+ null, null,
+ null, null, null, true, sentiment);
+ return true;
+ }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
+ }
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -158,6 +178,8 @@
mEntryManager = new TestableNotificationEntryManager(mContext, mBarService);
mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mCallback, mHeadsUpManager);
+
+ setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
}
@Test
@@ -196,6 +218,8 @@
assertEquals(mEntryManager.getNotificationData().get(mSbn.getKey()), entry);
assertNotNull(entry.row);
+ assertEquals(mEntry.userSentiment,
+ NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
}
@Test
@@ -204,6 +228,8 @@
mEntryManager.getNotificationData().add(mEntry);
+ setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
+
mHandler.post(() -> {
mEntryManager.updateNotification(mSbn, mRankingMap);
});
@@ -219,6 +245,8 @@
verify(mForegroundServiceController).updateNotification(eq(mSbn), anyInt());
verify(mCallback).onNotificationUpdated(mSbn);
assertNotNull(mEntry.row);
+ assertEquals(mEntry.userSentiment,
+ NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
index 6721938..d77bf69 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
@@ -16,11 +16,13 @@
package com.android.systemui.statusbar;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME;
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.any;
@@ -50,11 +52,11 @@
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.PollingCheck;
import android.testing.UiThreadTest;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
-import android.widget.Switch;
import android.widget.TextView;
import com.android.systemui.R;
@@ -66,7 +68,6 @@
import org.mockito.ArgumentCaptor;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -117,50 +118,31 @@
// Some test channels.
mNotificationChannel = new NotificationChannel(
- TEST_CHANNEL, TEST_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW);
+ TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW);
mDefaultNotificationChannel = new NotificationChannel(
NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME,
- NotificationManager.IMPORTANCE_LOW);
+ IMPORTANCE_LOW);
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
new Notification(), UserHandle.CURRENT, null, 0);
}
- private CharSequence getStringById(int resId) {
- return mContext.getString(resId);
+ // TODO: if tests are taking too long replace this with something that makes the animation
+ // finish instantly.
+ private void waitForUndoButton() {
+ PollingCheck.waitFor(1000,
+ () -> VISIBLE == mNotificationInfo.findViewById(R.id.confirmation).getVisibility());
}
-
- private CharSequence getNumChannelsDescString(int numChannels) {
- return String.format(
- mContext.getResources().getQuantityString(
- R.plurals.notification_num_channels_desc, numChannels),
- numChannels);
- }
-
- private CharSequence getChannelsListDescString(NotificationChannel... channels) {
- if (channels.length == 2) {
- return mContext.getString(R.string.notification_channels_list_desc_2,
- channels[0].getName(), channels[1].getName());
- } else {
- final int numOthers = channels.length - 2;
- return String.format(
- mContext.getResources().getQuantityString(
- R.plurals.notification_channels_list_desc_2_and_others, numOthers),
- channels[0].getName(), channels[1].getName(), numOthers);
- }
- }
-
- private CharSequence getNumChannelsString(int numChannels) {
- return mContext.getString(R.string.notification_num_channels, numChannels);
+ private void waitForStopButton() {
+ PollingCheck.waitFor(1000,
+ () -> VISIBLE == mNotificationInfo.findViewById(R.id.prompt).getVisibility());
}
@Test
public void testBindNotification_SetsTextApplicationName() throws Exception {
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, null);
- final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.pkgname);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+ final TextView textView = mNotificationInfo.findViewById(R.id.pkgname);
assertTrue(textView.getText().toString().contains("App Name"));
}
@@ -170,24 +152,19 @@
when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class)))
.thenReturn(iconDrawable);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, null);
- final ImageView iconView = (ImageView) mNotificationInfo.findViewById(R.id.pkgicon);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+ final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon);
assertEquals(iconDrawable, iconView.getDrawable());
}
@Test
public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, null);
- final TextView groupNameView = (TextView) mNotificationInfo.findViewById(R.id.group_name);
- assertEquals(View.GONE, groupNameView.getVisibility());
- final TextView groupDividerView =
- (TextView) mNotificationInfo.findViewById(R.id.pkg_group_divider);
- assertEquals(View.GONE, groupDividerView.getVisibility());
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+ final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
+ assertEquals(GONE, groupNameView.getVisibility());
+ final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider);
+ assertEquals(GONE, groupDividerView.getVisibility());
}
@Test
@@ -199,75 +176,50 @@
eq("test_group_id"), eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
.thenReturn(notificationChannelGroup);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, null);
- final TextView groupNameView = (TextView) mNotificationInfo.findViewById(R.id.group_name);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+ final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(View.VISIBLE, groupNameView.getVisibility());
assertEquals("Test Group Name", groupNameView.getText());
- final TextView groupDividerView =
- (TextView) mNotificationInfo.findViewById(R.id.pkg_group_divider);
+ final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider);
assertEquals(View.VISIBLE, groupDividerView.getVisibility());
}
@Test
public void testBindNotification_SetsTextChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, null);
- final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.channel_name);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+ final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(TEST_CHANNEL_NAME, textView.getText());
}
@Test
public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, null);
- final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.channel_name);
- assertEquals(mContext.getString(R.string.notification_header_default_channel),
- textView.getText());
+ TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, null);
+ final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
+ assertEquals(GONE, textView.getVisibility());
}
@Test
- public void testBindNotification_UnblockablePackageDoesNotUseChannelName() throws Exception {
+ public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, Collections.singleton(TEST_PACKAGE_NAME));
- final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.channel_name);
- assertEquals(mContext.getString(R.string.notification_header_default_channel),
- textView.getText());
- }
-
- @Test
- public void testBindNotification_DefaultChannelUsesNameWhenMoreThanOneChannelExists()
- throws Exception {
- when(mMockINotificationManager.getNumNotificationChannelsForPackage(
- eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(2);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, null);
- final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.channel_name);
- assertEquals(mDefaultNotificationChannel.getName(), textView.getText());
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null,
+ Collections.singleton(TEST_PACKAGE_NAME));
+ final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
+ assertEquals(VISIBLE, textView.getVisibility());
}
@Test
public void testBindNotification_SetsOnClickListenerForSettings() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
latch.countDown();
- }, null, null, null, null);
+ }, null, null);
- final TextView settingsButton =
- (TextView) mNotificationInfo.findViewById(R.id.more_settings);
+ final View settingsButton = mNotificationInfo.findViewById(R.id.info);
settingsButton.performClick();
// Verify that listener was triggered.
assertEquals(0, latch.getCount());
@@ -276,24 +228,20 @@
@Test
public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null, null, null);
- final TextView settingsButton =
- (TextView) mNotificationInfo.findViewById(R.id.more_settings);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+ final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@Test
- public void testBindNotification_SettingsButtonReappersAfterSecondBind() throws Exception {
+ public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null, null, null);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn,
- (View v, NotificationChannel c, int appUid) -> {}, null, null, null, null);
- final TextView settingsButton =
- (TextView) mNotificationInfo.findViewById(R.id.more_settings);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
+ (View v, NotificationChannel c, int appUid) -> {
+ }, null, null);
+ final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertEquals(View.VISIBLE, settingsButton.getVisibility());
}
@@ -301,279 +249,63 @@
public void testOnClickListenerPassesNullChannelForBundle() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME,
- Arrays.asList(mNotificationChannel, mDefaultNotificationChannel),
- mNotificationChannel.getImportance(), mSbn,
+ TEST_PACKAGE_NAME, mNotificationChannel, 2, mSbn, null,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(null, c);
latch.countDown();
- }, null, null, null, null);
+ }, null, null);
- final TextView settingsButton =
- (TextView) mNotificationInfo.findViewById(R.id.more_settings);
- settingsButton.performClick();
+ mNotificationInfo.findViewById(R.id.info).performClick();
// Verify that listener was triggered.
assertEquals(0, latch.getCount());
}
@Test
- public void testBindNotification_SettingsTextWithOneChannel() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn,
- (View v, NotificationChannel c, int appUid) -> {
- }, null, null, null, null);
- final TextView settingsButton =
- (TextView) mNotificationInfo.findViewById(R.id.more_settings);
- assertEquals(getStringById(R.string.notification_more_settings), settingsButton.getText());
- }
-
- @Test
- public void testBindNotification_SettingsTextWithMultipleChannels() throws Exception {
- when(mMockINotificationManager.getNumNotificationChannelsForPackage(
- eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(2);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn,
- (View v, NotificationChannel c, int appUid) -> {
- }, null, null, null, null);
- final TextView settingsButton =
- (TextView) mNotificationInfo.findViewById(R.id.more_settings);
- assertEquals(getStringById(R.string.notification_all_categories), settingsButton.getText());
- }
-
- @Test
- public void testBindNotification_SettingsTextWithMultipleChannelsForUnblockableApp()
- throws Exception {
- when(mMockINotificationManager.getNumNotificationChannelsForPackage(
- eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(2);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn,
- (View v, NotificationChannel c, int appUid) -> {
- }, null, null, null, Collections.singleton(TEST_PACKAGE_NAME));
- final TextView settingsButton =
- (TextView) mNotificationInfo.findViewById(R.id.more_settings);
- assertEquals(getStringById(R.string.notification_more_settings), settingsButton.getText());
- }
-
- @Test
- public void testBindNotification_SetsOnClickListenerForDone() throws Exception {
- final CountDownLatch latch = new CountDownLatch(1);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null,
- null, (View v) -> {
- latch.countDown();
- },
- null, null);
-
- final TextView doneButton = (TextView) mNotificationInfo.findViewById(R.id.done);
- doneButton.performClick();
- // Verify that listener was triggered.
- assertEquals(0, latch.getCount());
- }
-
- @Test
- public void testBindNotification_NumChannelsTextHiddenWhenDefaultChannel() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null,
- null, null, null);
- final TextView numChannelsView =
- (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
- assertEquals(View.INVISIBLE, numChannelsView.getVisibility());
- }
-
- @Test
- public void testBindNotification_NumChannelsTextDisplaysWhenMoreThanOneChannelExists()
- throws Exception {
- when(mMockINotificationManager.getNumNotificationChannelsForPackage(
- eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(2);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null,
- null, null, null);
- final TextView numChannelsView =
- (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
- assertEquals(numChannelsView.getVisibility(), View.VISIBLE);
- assertEquals(getNumChannelsDescString(2), numChannelsView.getText());
- }
-
- @Test
- public void testBindNotification_NumChannelsTextDisplaysWhenNotDefaultChannel()
- throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, null);
- final TextView numChannelsView =
- (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
- assertEquals(numChannelsView.getVisibility(), View.VISIBLE);
- assertEquals(getNumChannelsDescString(1), numChannelsView.getText());
- }
-
- @Test
- public void testBindNotification_NumChannelsTextScalesWithNumberOfChannels()
- throws Exception {
- when(mMockINotificationManager.getNumNotificationChannelsForPackage(
- eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(2);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, null);
- final TextView numChannelsView =
- (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
- assertEquals(getNumChannelsDescString(2), numChannelsView.getText());
- }
-
- @Test
@UiThreadTest
- public void testBindNotification_NumChannelsTextListsChannelsWhenTwoInBundle()
+ public void testBindNotification_ChannelNameInvisibleWhenBundleFromDifferentChannels()
throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null, null, null);
- final TextView numChannelsView =
- (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
- assertEquals(getChannelsListDescString(mNotificationChannel, mDefaultNotificationChannel),
- numChannelsView.getText());
- }
-
- @Test
- @UiThreadTest
- public void testBindNotification_NumChannelsTextListsChannelsWhenThreeInBundle()
- throws Exception {
- NotificationChannel thirdChannel = new NotificationChannel(
- "third_channel", "third_channel", NotificationManager.IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME,
- Arrays.asList(mNotificationChannel, mDefaultNotificationChannel, thirdChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null, null, null);
- final TextView numChannelsView =
- (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
- assertEquals(
- getChannelsListDescString(mNotificationChannel, mDefaultNotificationChannel,
- thirdChannel),
- numChannelsView.getText());
- }
-
- @Test
- @UiThreadTest
- public void testBindNotification_NumChannelsTextListsChannelsWhenFourInBundle()
- throws Exception {
- NotificationChannel thirdChannel = new NotificationChannel(
- "third_channel", "third_channel", NotificationManager.IMPORTANCE_LOW);
- NotificationChannel fourthChannel = new NotificationChannel(
- "fourth_channel", "fourth_channel", NotificationManager.IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME,
- Arrays.asList(mNotificationChannel, mDefaultNotificationChannel, thirdChannel,
- fourthChannel), mNotificationChannel.getImportance(), mSbn, null, null,
- null, null, null);
- final TextView numChannelsView =
- (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
- assertEquals(
- getChannelsListDescString(mNotificationChannel, mDefaultNotificationChannel,
- thirdChannel, fourthChannel),
- numChannelsView.getText());
- }
-
- @Test
- @UiThreadTest
- public void testBindNotification_ChannelNameChangesWhenBundleFromDifferentChannels()
- throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null, null, null);
+ TEST_PACKAGE_NAME, mNotificationChannel, 2, mSbn, null, null, null, null);
final TextView channelNameView =
- (TextView) mNotificationInfo.findViewById(R.id.channel_name);
- assertEquals(getNumChannelsString(2), channelNameView.getText());
+ mNotificationInfo.findViewById(R.id.channel_name);
+ assertEquals(GONE, channelNameView.getVisibility());
}
@Test
@UiThreadTest
- public void testEnabledSwitchInvisibleIfBundleFromDifferentChannels() throws Exception {
+ public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null, null, null);
- Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
- assertEquals(View.INVISIBLE, enabledSwitch.getVisibility());
- }
-
- @Test
- public void testbindNotification_ChannelDisabledTextGoneWhenNotDisabled() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null, null, null);
- final TextView channelDisabledView =
- (TextView) mNotificationInfo.findViewById(R.id.channel_disabled);
- assertEquals(channelDisabledView.getVisibility(), View.GONE);
- }
-
- @Test
- public void testbindNotification_ChannelDisabledTextVisibleWhenDisabled() throws Exception {
- mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, null);
- final TextView channelDisabledView =
- (TextView) mNotificationInfo.findViewById(R.id.channel_disabled);
- assertEquals(channelDisabledView.getVisibility(), View.VISIBLE);
- // Replaces the numChannelsView
- final TextView numChannelsView =
- (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
- assertEquals(numChannelsView.getVisibility(), View.GONE);
+ TEST_PACKAGE_NAME, mNotificationChannel, 2, mSbn, null, null, null, null);
+ final TextView blockView = mNotificationInfo.findViewById(R.id.block);
+ assertEquals(GONE, blockView.getVisibility());
}
@Test
public void testbindNotification_UnblockableTextVisibleWhenAppUnblockable() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null,
null, Collections.singleton(TEST_PACKAGE_NAME));
- final TextView numChannelsView =
- (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
- assertEquals(View.VISIBLE, numChannelsView.getVisibility());
+ final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
+ assertEquals(View.VISIBLE, view.getVisibility());
assertEquals(mContext.getString(R.string.notification_unblockable_desc),
- numChannelsView.getText());
- }
-
- @Test
- @UiThreadTest
- public void testBindNotification_ChannelDisabledTextShowsForDefaultChannel()
- throws Exception {
- mDefaultNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel),
- mDefaultNotificationChannel.getImportance(), mSbn, null, null,
- null, null, null);
- final TextView channelDisabledView =
- (TextView) mNotificationInfo.findViewById(R.id.channel_disabled);
- assertEquals(View.VISIBLE, channelDisabledView.getVisibility());
+ view.getText());
}
@Test
public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, null);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), any());
}
@Test
public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception {
- mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+ mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, null);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
- Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
- enabledSwitch.setChecked(false);
+ mNotificationInfo.findViewById(R.id.block).performClick();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), any());
}
@@ -582,9 +314,7 @@
public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnchanged()
throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, null);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
mNotificationInfo.handleCloseControls(true, false);
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
@@ -596,9 +326,7 @@
throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, null);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
mNotificationInfo.handleCloseControls(true, false);
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
@@ -606,114 +334,25 @@
}
@Test
- public void testEnabledSwitchOnByDefault() throws Exception {
- mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, null);
-
- Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
- assertTrue(enabledSwitch.isChecked());
- }
-
- @Test
- public void testEnabledButtonOffWhenAlreadyBanned() throws Exception {
- mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, null);
-
- Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
- assertFalse(enabledSwitch.isChecked());
- }
-
- @Test
- public void testEnabledSwitchVisibleByDefault() throws Exception {
- mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, null);
-
- Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
- assertEquals(View.VISIBLE, enabledSwitch.getVisibility());
- }
-
- @Test
- public void testEnabledSwitchInvisibleIfNonBlockable() throws Exception {
- mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, Collections.singleton(TEST_PACKAGE_NAME));
-
- Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
- assertEquals(View.INVISIBLE, enabledSwitch.getVisibility());
- }
-
- @Test
- public void testEnabledSwitchInvisibleIfNonBlockableSystemChannel() throws Exception {
- mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
- mNotificationChannel.setBlockableSystem(false);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_SYSTEM_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, null);
-
- Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
- assertEquals(View.INVISIBLE, enabledSwitch.getVisibility());
- }
-
- @Test
- public void testEnabledSwitchVisibleIfBlockableSystemChannel() throws Exception {
- mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
- mNotificationChannel.setBlockableSystem(true);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_SYSTEM_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, null);
-
- Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
- assertEquals(View.VISIBLE, enabledSwitch.getVisibility());
- }
-
- @Test
- public void testEnabledSwitchInvisibleIfMultiChannelSummary() throws Exception {
- mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
- mNotificationChannel.setBlockableSystem(true);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, Collections.singleton(TEST_PACKAGE_NAME));
-
- Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
- assertEquals(View.INVISIBLE, enabledSwitch.getVisibility());
- }
-
- @Test
public void testNonBlockableAppDoesNotBecomeBlocked() throws Exception {
- mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+ mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null,
null, Collections.singleton(TEST_PACKAGE_NAME));
- mNotificationInfo.handleCloseControls(true, false);
+ mNotificationInfo.findViewById(R.id.block).performClick();
+ waitForUndoButton();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), any());
}
@Test
- public void testEnabledSwitchChangedCallsUpdateNotificationChannel() throws Exception {
- mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+ public void testBlockChangedCallsUpdateNotificationChannel() throws Exception {
+ mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, Collections.singleton(TEST_PACKAGE_NAME));
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
- Switch enabledSwitch = mNotificationInfo.findViewById(R.id.channel_enabled_switch);
- enabledSwitch.setChecked(false);
+ mNotificationInfo.findViewById(R.id.block).performClick();
+ waitForUndoButton();
mNotificationInfo.handleCloseControls(true, false);
ArgumentCaptor<NotificationChannel> updated =
@@ -725,15 +364,35 @@
}
@Test
- public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception {
- mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+ public void testBlockUndoDoesNotCallUpdateNotificationChannel() throws Exception {
+ mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
- null, Collections.singleton(TEST_PACKAGE_NAME));
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null,
+ Collections.singleton(TEST_PACKAGE_NAME));
- Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
- enabledSwitch.setChecked(false);
+
+ mNotificationInfo.findViewById(R.id.block).performClick();
+ waitForUndoButton();
+ mNotificationInfo.findViewById(R.id.undo).performClick();
+ waitForStopButton();
+ mNotificationInfo.handleCloseControls(true, false);
+
+ ArgumentCaptor<NotificationChannel> updated =
+ ArgumentCaptor.forClass(NotificationChannel.class);
+ verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
+ anyString(), eq(TEST_UID), updated.capture());
+ assertEquals(IMPORTANCE_LOW, mNotificationChannel.getImportance());
+ }
+
+ @Test
+ public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception {
+ mNotificationChannel.setImportance(IMPORTANCE_LOW);
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null,
+ Collections.singleton(TEST_PACKAGE_NAME));
+
+ mNotificationInfo.findViewById(R.id.block).performClick();
+ waitForUndoButton();
mNotificationInfo.handleCloseControls(false, false);
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel));
@@ -741,16 +400,14 @@
@Test
public void testCloseControlsDoesNotUpdateIfCheckSaveListenerIsNoOp() throws Exception {
- mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+ mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
(Runnable saveImportance, StatusBarNotification sbn) -> {
- },
- Collections.singleton(TEST_PACKAGE_NAME));
+ }, null, null, Collections.singleton(TEST_PACKAGE_NAME));
- Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
- enabledSwitch.setChecked(false);
+ mNotificationInfo.findViewById(R.id.block).performClick();
+ waitForUndoButton();
mNotificationInfo.handleCloseControls(true, false);
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel));
@@ -758,17 +415,15 @@
@Test
public void testCloseControlsUpdatesWhenCheckSaveListenerUsesCallback() throws Exception {
- mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+ mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), mSbn, null, null, null,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
(Runnable saveImportance, StatusBarNotification sbn) -> {
saveImportance.run();
- },
- Collections.singleton(TEST_PACKAGE_NAME));
+ }, null, null, null);
- Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
- enabledSwitch.setChecked(false);
+ mNotificationInfo.findViewById(R.id.block).performClick();
+ waitForUndoButton();
mNotificationInfo.handleCloseControls(true, false);
verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel));
@@ -785,22 +440,19 @@
List<ResolveInfo> ris = new ArrayList<>();
ris.add(ri);
when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
- mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+ mNotificationChannel.setImportance(IMPORTANCE_LOW);
Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
.setSettingsText(settingsText).build();
StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), sbn, null,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null,
(View v, Intent intent) -> {
latch.countDown();
- }, null, null, null);
+ }, null);
final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
assertEquals(View.VISIBLE, settingsLink.getVisibility());
- assertTrue(settingsLink.getText().toString().length() > settingsText.length());
- assertTrue(settingsLink.getText().toString().contains(settingsText));
settingsLink.performClick();
assertEquals(0, latch.getCount());
}
@@ -816,17 +468,17 @@
List<ResolveInfo> ris = new ArrayList<>();
ris.add(ri);
when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
- mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+ mNotificationChannel.setImportance(IMPORTANCE_LOW);
Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
.setSettingsText(settingsText).build();
StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel),
- mNotificationChannel.getImportance(), sbn, null, (View v, Intent intent) -> {
+ TEST_PACKAGE_NAME, mNotificationChannel, 2, sbn, null, null,
+ (View v, Intent intent) -> {
latch.countDown();
- }, null, null, null);
+ }, null);
final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
assertEquals(View.VISIBLE, settingsLink.getVisibility());
settingsLink.performClick();
@@ -837,18 +489,16 @@
public void testNoSettingsLink_noHandlingActivity() throws Exception {
final String settingsText = "work chats";
when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(null);
- mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+ mNotificationChannel.setImportance(IMPORTANCE_LOW);
Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
.setSettingsText(settingsText).build();
StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), sbn, null, null, null,
- null, null);
+ TEST_PACKAGE_NAME, mNotificationChannel, 2, sbn, null, null, null, null);
final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
- assertEquals(View.GONE, settingsLink.getVisibility());
+ assertEquals(GONE, settingsLink.getVisibility());
}
@Test
@@ -860,52 +510,17 @@
List<ResolveInfo> ris = new ArrayList<>();
ris.add(ri);
when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
- mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+ mNotificationChannel.setImportance(IMPORTANCE_LOW);
Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()).build();
StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), sbn, null, null, null,
- null, null);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, null);
final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
- assertEquals(View.GONE, settingsLink.getVisibility());
+ assertEquals(GONE, settingsLink.getVisibility());
}
- @Test
- public void testNoSettingsLink_afterBlockingChannel() throws Exception {
- final String settingsText = "work chats";
- final ResolveInfo ri = new ResolveInfo();
- ri.activityInfo = new ActivityInfo();
- ri.activityInfo.packageName = TEST_PACKAGE_NAME;
- ri.activityInfo.name = "something";
- List<ResolveInfo> ris = new ArrayList<>();
- ris.add(ri);
- when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
- mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
- Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
- .setSettingsText(settingsText).build();
- StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
- 0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
-
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
- mNotificationChannel.getImportance(), sbn, null, null, null,
- null, null);
- final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
- assertEquals(View.VISIBLE, settingsLink.getVisibility());
-
- // Block channel
- Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
- enabledSwitch.setChecked(false);
-
- assertEquals(View.GONE, settingsLink.getVisibility());
-
- //unblock
- enabledSwitch.setChecked(true);
- assertEquals(View.VISIBLE, settingsLink.getVisibility());
- }
@Test
public void testWillBeRemovedReturnsFalseBeforeBind() throws Exception {
diff --git a/packages/VpnDialogs/res/values-hi/strings.xml b/packages/VpnDialogs/res/values-hi/strings.xml
index 7091ce7..fc4fdcd 100644
--- a/packages/VpnDialogs/res/values-hi/strings.xml
+++ b/packages/VpnDialogs/res/values-hi/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"कनेक्शन अनुरोध"</string>
- <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> VPN कनेक्शन सेट करना चाहता है जो उसे नेटवर्क ट्रैफ़िक मॉनीटर करने देता है. केवल तभी स्वीकार करें, जबकि आप स्रोत पर भरोसा करते हों. VPN सक्रिय होने पर आपकी स्क्रीन पर ऊपर <br /> <br /> <img src=vpn_icon /> दिखाई देता है."</string>
+ <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> VPN कनेक्शन सेट करना चाहता है जिससे वह नेटवर्क ट्रैफ़िक पर नज़र रख पाएगा. इसकी मंज़ूरी तभी दें जब आपको आप इसपर भरोसा हो. VPN चालू होने पर आपकी स्क्रीन के सबसे ऊपर <br /> <br /> <img src=vpn_icon /> दिखाई देता है."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN कनेक्ट है"</string>
<string name="session" msgid="6470628549473641030">"सत्र:"</string>
<string name="duration" msgid="3584782459928719435">"अवधि:"</string>
diff --git a/packages/VpnDialogs/res/values-nb/strings.xml b/packages/VpnDialogs/res/values-nb/strings.xml
index b45d3b9..be572d4 100644
--- a/packages/VpnDialogs/res/values-nb/strings.xml
+++ b/packages/VpnDialogs/res/values-nb/strings.xml
@@ -32,5 +32,5 @@
<string name="configure" msgid="4905518375574791375">"Konfigurer"</string>
<string name="disconnect" msgid="971412338304200056">"Koble fra"</string>
<string name="open_app" msgid="3717639178595958667">"Åpne appen"</string>
- <string name="dismiss" msgid="6192859333764711227">"Avvis"</string>
+ <string name="dismiss" msgid="6192859333764711227">"Lukk"</string>
</resources>
diff --git a/packages/VpnDialogs/res/values-pl/strings.xml b/packages/VpnDialogs/res/values-pl/strings.xml
index be6a4c2..d5201d7 100644
--- a/packages/VpnDialogs/res/values-pl/strings.xml
+++ b/packages/VpnDialogs/res/values-pl/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Żądanie połączenia"</string>
- <string name="warning" msgid="809658604548412033">"Aplikacja <xliff:g id="APP">%s</xliff:g> chce utworzyć połączenie VPN, które pozwoli jej na monitorowanie ruchu sieciowego. Zaakceptuj, tylko jeśli masz zaufanie do źródła. Gdy sieć VPN jest aktywna, u góry ekranu pojawia się <br /> <br /> <img src=vpn_icon />."</string>
+ <string name="warning" msgid="809658604548412033">"Aplikacja <xliff:g id="APP">%s</xliff:g> chce utworzyć połączenie VPN, które pozwoli jej na monitorowanie ruchu sieciowego. Zaakceptuj, tylko jeśli masz zaufanie do źródła. <br /> <br />Gdy sieć VPN jest aktywna, u góry ekranu pojawia się <img src=vpn_icon />."</string>
<string name="legacy_title" msgid="192936250066580964">"Połączono z VPN"</string>
<string name="session" msgid="6470628549473641030">"Sesja:"</string>
<string name="duration" msgid="3584782459928719435">"Czas trwania:"</string>
diff --git a/packages/VpnDialogs/res/values-ru/strings.xml b/packages/VpnDialogs/res/values-ru/strings.xml
index b9207a0..35ae7f7 100644
--- a/packages/VpnDialogs/res/values-ru/strings.xml
+++ b/packages/VpnDialogs/res/values-ru/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Запрос на подключение"</string>
- <string name="warning" msgid="809658604548412033">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" пытается подключиться к сети VPN, чтобы отслеживать трафик. Этот запрос следует принимать, только если вы доверяете источнику.<br/><br/>Когда подключение VPN активно, в верхней части экрана появляется значок <img src=vpn_icon />."</string>
+ <string name="warning" msgid="809658604548412033">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" пытается подключиться к сети VPN, чтобы отслеживать трафик. Этот запрос следует принимать, только если вы доверяете источнику. <br /> <br />Когда подключение VPN активно, в верхней части экрана появляется значок <img src=vpn_icon />."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN-подключение установлено"</string>
<string name="session" msgid="6470628549473641030">"Сеанс:"</string>
<string name="duration" msgid="3584782459928719435">"Продолжительность:"</string>
diff --git a/packages/VpnDialogs/res/values-zh-rCN/strings.xml b/packages/VpnDialogs/res/values-zh-rCN/strings.xml
index 2815312..0bd62ce 100644
--- a/packages/VpnDialogs/res/values-zh-rCN/strings.xml
+++ b/packages/VpnDialogs/res/values-zh-rCN/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"网络连接请求"</string>
- <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g>想要设置一个VPN连接(可被用于监控网络流量)。请只在您信任该来源的情况下才接受此请求。在VPN处于活动状态时,您的屏幕顶部会显示 <img src=vpn_icon /> 图标。"</string>
+ <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g>想要设置一个 VPN 连接,以允许其监控网络流量。除非您信任该来源,否则请勿接受此请求。启用 VPN 时,您的屏幕顶部会显示 <img src=vpn_icon /> 图标。"</string>
<string name="legacy_title" msgid="192936250066580964">"已连接VPN"</string>
<string name="session" msgid="6470628549473641030">"会话:"</string>
<string name="duration" msgid="3584782459928719435">"时长:"</string>
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationOverlay/Android.mk
new file mode 100644
index 0000000..f4205ad6
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationOverlay/Android.mk
@@ -0,0 +1,13 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := DisplayCutoutEmulation
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := DisplayCutoutEmulationOverlay
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/AndroidManifest.xml b/packages/overlays/DisplayCutoutEmulationOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..dd43690
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationOverlay/AndroidManifest.xml
@@ -0,0 +1,8 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.internal.display.cutout.emulation"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="android" android:priority="1"/>
+
+ <application android:label="@string/display_cutout_emulation_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml
new file mode 100644
index 0000000..88c19c7
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml
@@ -0,0 +1,44 @@
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- The bounding path of the cutout region of the main built-in display.
+ Must either be empty if there is no cutout region, or a string that is parsable by
+ {@link android.util.PathParser}. -->
+ <string translatable="false" name="config_mainBuiltInDisplayCutout">
+ M 687.0,0
+ l -66,50
+ l 0,50
+ l 66,50
+ l 66,0
+ l 66,-50
+ l 0,-50
+ l -66,-50
+ z
+ </string>
+
+ <!-- Whether the display cutout region of the main built-in display should be forced to
+ black in software (to avoid aliasing or emulate a cutout that is not physically existent).
+ -->
+ <bool name="config_fillMainBuiltInDisplayCutout">true</bool>
+
+ <!-- Height of the status bar -->
+ <dimen name="status_bar_height">150px</dimen>
+
+</resources>
+
+
diff --git a/core/java/android/os/Seccomp.java b/packages/overlays/DisplayCutoutEmulationOverlay/res/values/strings.xml
similarity index 61%
copy from core/java/android/os/Seccomp.java
copy to packages/overlays/DisplayCutoutEmulationOverlay/res/values/strings.xml
index f14e93f..5d5c425 100644
--- a/core/java/android/os/Seccomp.java
+++ b/packages/overlays/DisplayCutoutEmulationOverlay/res/values/strings.xml
@@ -1,11 +1,13 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2017, 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
+ * 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,
@@ -13,12 +15,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-package android.os;
+ <string name="display_cutout_emulation_overlay">Display Cutout Emulation</string>
-/**
- * @hide
- */
-public final class Seccomp {
- public static final native void setPolicy();
-}
+</resources>
+
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 4f04d36..04cee67 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5129,6 +5129,21 @@
// OS: P
FUELGAUGE_SMART_BATTERY = 1281;
+ // ACTION: User tapped Screenshot in the power menu.
+ // CATEGORY: GLOBAL_SYSTEM_UI
+ // OS: P
+ ACTION_SCREENSHOT_POWER_MENU = 1282;
+
+ // OPEN: Settings > Apps & Notifications -> Special app access -> Storage Access
+ // CATEGORY: SETTINGS
+ // OS: P
+ STORAGE_ACCESS = 1283;
+
+ // OPEN: Settings > Apps & Notifications -> Special app access -> Storage Access -> Package
+ // CATEGORY: SETTINGS
+ // OS: P
+ APPLICATIONS_STORAGE_DETAIL = 1284;
+
// ---- End P Constants, all P constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index ba8ce59..fc6058c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1280,7 +1280,7 @@
}
int servicePackageUid = serviceInfo.applicationInfo.uid;
- if (mAppOpsManager.noteOpNoThrow(AppOpsManager.OP_BIND_ACCESSIBILITY_SERVICE,
+ if (mAppOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE,
servicePackageUid, serviceInfo.packageName) != AppOpsManager.MODE_ALLOWED) {
Slog.w(LOG_TAG, "Skipping accessibility service " + new ComponentName(
serviceInfo.packageName, serviceInfo.name).flattenToShortString()
@@ -1362,14 +1362,13 @@
private int computeRelevantEventTypes(UserState userState, Client client) {
int relevantEventTypes = 0;
- int numBoundServices = userState.mBoundServices.size();
- for (int i = 0; i < numBoundServices; i++) {
- AccessibilityServiceConnection service =
- userState.mBoundServices.get(i);
+ // Use iterator for thread-safety
+ for (AccessibilityServiceConnection service : userState.mBoundServices) {
relevantEventTypes |= isClientInPackageWhitelist(service.getServiceInfo(), client)
? service.getRelevantEventTypes()
: 0;
}
+
relevantEventTypes |= isClientInPackageWhitelist(
mUiAutomationManager.getServiceInfo(), client)
? mUiAutomationManager.getRelevantEventTypes()
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 74d2ddd..0a03b7f 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -17,6 +17,7 @@
package com.android.server.accessibility;
import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
+import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
@@ -364,7 +365,7 @@
persistScaleAndTransitionTo(mViewportDraggingState);
- } else if (action == ACTION_UP) {
+ } else if (action == ACTION_UP || action == ACTION_CANCEL) {
persistScaleAndTransitionTo(mDetectingState);
@@ -496,7 +497,9 @@
}
}
break;
- case ACTION_UP: {
+
+ case ACTION_UP:
+ case ACTION_CANCEL: {
if (!mZoomedInBeforeDrag) zoomOff();
clear();
transitionTo(mDetectingState);
@@ -533,12 +536,15 @@
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- if (event.getActionMasked() == ACTION_UP) {
- transitionTo(mDetectingState);
- }
+ switch (event.getActionMasked()) {
+ case ACTION_UP:
+ case ACTION_CANCEL: {
+ transitionTo(mDetectingState);
+ } break;
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mLastDelegatedDownEventTime = event.getDownTime();
+ case ACTION_DOWN: {
+ mLastDelegatedDownEventTime = event.getDownTime();
+ } break;
}
if (getNext() != null) {
// We cache some events to see if the user wants to trigger magnification.
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 307f74d..f96fa7c2 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -231,8 +231,7 @@
final Window window = mDialog.getWindow();
window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
- window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+ window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS);
diff --git a/services/backup/java/com/android/server/backup/BackupPolicyEnforcer.java b/services/backup/java/com/android/server/backup/BackupPolicyEnforcer.java
new file mode 100644
index 0000000..158084a
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/BackupPolicyEnforcer.java
@@ -0,0 +1,25 @@
+package com.android.server.backup;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * A helper class to decouple this service from {@link DevicePolicyManager} in order to improve
+ * testability.
+ */
+@VisibleForTesting
+public class BackupPolicyEnforcer {
+ private DevicePolicyManager mDevicePolicyManager;
+
+ public BackupPolicyEnforcer(Context context) {
+ mDevicePolicyManager =
+ (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ }
+
+ public ComponentName getMandatoryBackupTransport() {
+ return mDevicePolicyManager.getMandatoryBackupTransport();
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index 03591a8..5188910 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -38,6 +38,7 @@
import android.app.IActivityManager;
import android.app.IBackupAgent;
import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
import android.app.backup.BackupManager;
import android.app.backup.BackupManagerMonitor;
import android.app.backup.FullBackup;
@@ -207,6 +208,10 @@
public static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
public static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
+ // Time delay for initialization operations that can be delayed so as not to consume too much CPU
+ // on bring-up and increase time-to-UI.
+ private static final long INITIALIZATION_DELAY_MILLIS = 3000;
+
// Timeout interval for deciding that a bind or clear-data has taken too long
private static final long TIMEOUT_INTERVAL = 10 * 1000;
@@ -282,6 +287,9 @@
private final BackupPasswordManager mBackupPasswordManager;
+ // Time when we post the transport registration operation
+ private final long mRegisterTransportsRequestedTime;
+
@GuardedBy("mPendingRestores")
private boolean mIsRestoreInProgress;
@GuardedBy("mPendingRestores")
@@ -674,6 +682,8 @@
@GuardedBy("mQueueLock")
private ArrayList<FullBackupEntry> mFullBackupQueue;
+ private BackupPolicyEnforcer mBackupPolicyEnforcer;
+
// Utility: build a new random integer token. The low bits are the ordinal of the
// operation for near-time uniqueness, and the upper bits are random for app-
// side unpredictability.
@@ -735,6 +745,9 @@
// Set up our transport options and initialize the default transport
SystemConfig systemConfig = SystemConfig.getInstance();
Set<ComponentName> transportWhitelist = systemConfig.getBackupTransportWhitelist();
+ if (transportWhitelist == null) {
+ transportWhitelist = Collections.emptySet();
+ }
String transport =
Settings.Secure.getString(
@@ -749,8 +762,7 @@
new TransportManager(
context,
transportWhitelist,
- transport,
- backupThread.getLooper());
+ transport);
// If encrypted file systems is enabled or disabled, this call will return the
// correct directory.
@@ -852,15 +864,19 @@
}
mTransportManager = transportManager;
- mTransportManager.setTransportBoundListener(mTransportBoundListener);
- mTransportManager.registerAllTransports();
+ mTransportManager.setOnTransportRegisteredListener(this::onTransportRegistered);
+ mRegisterTransportsRequestedTime = SystemClock.elapsedRealtime();
+ mBackupHandler.postDelayed(
+ mTransportManager::registerTransports, INITIALIZATION_DELAY_MILLIS);
- // Now that we know about valid backup participants, parse any
- // leftover journal files into the pending backup set
- mBackupHandler.post(this::parseLeftoverJournals);
+ // Now that we know about valid backup participants, parse any leftover journal files into
+ // the pending backup set
+ mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS);
// Power management
mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
+
+ mBackupPolicyEnforcer = new BackupPolicyEnforcer(context);
}
private void initPackageTracking() {
@@ -1151,39 +1167,28 @@
}
}
- private TransportManager.TransportBoundListener mTransportBoundListener =
- new TransportManager.TransportBoundListener() {
- @Override
- public boolean onTransportBound(IBackupTransport transport) {
- // If the init sentinel file exists, we need to be sure to perform the init
- // as soon as practical. We also create the state directory at registration
- // time to ensure it's present from the outset.
- String name = null;
- try {
- name = transport.name();
- String transportDirName = transport.transportDirName();
- File stateDir = new File(mBaseStateDir, transportDirName);
- stateDir.mkdirs();
+ private void onTransportRegistered(String transportName, String transportDirName) {
+ if (DEBUG) {
+ long timeMs = SystemClock.elapsedRealtime() - mRegisterTransportsRequestedTime;
+ Slog.d(TAG, "Transport " + transportName + " registered " + timeMs
+ + "ms after first request (delay = " + INITIALIZATION_DELAY_MILLIS + "ms)");
+ }
- File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
- if (initSentinel.exists()) {
- synchronized (mQueueLock) {
- mPendingInits.add(name);
+ File stateDir = new File(mBaseStateDir, transportDirName);
+ stateDir.mkdirs();
- // TODO: pick a better starting time than now + 1 minute
- long delay = 1000 * 60; // one minute, in milliseconds
- mAlarmManager.set(AlarmManager.RTC_WAKEUP,
- System.currentTimeMillis() + delay, mRunInitIntent);
- }
- }
- return true;
- } catch (Exception e) {
- // the transport threw when asked its file naming prefs; declare it invalid
- Slog.w(TAG, "Failed to regiser transport: " + name);
- return false;
- }
- }
- };
+ File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
+ if (initSentinel.exists()) {
+ synchronized (mQueueLock) {
+ mPendingInits.add(transportName);
+
+ // TODO: pick a better starting time than now + 1 minute
+ long delay = 1000 * 60; // one minute, in milliseconds
+ mAlarmManager.set(AlarmManager.RTC_WAKEUP,
+ System.currentTimeMillis() + delay, mRunInitIntent);
+ }
+ }
+ }
// ----- Track installation/removal of packages -----
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -2774,6 +2779,10 @@
public void setBackupEnabled(boolean enable) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"setBackupEnabled");
+ if (!enable && mBackupPolicyEnforcer.getMandatoryBackupTransport() != null) {
+ Slog.w(TAG, "Cannot disable backups when the mandatory backups policy is active.");
+ return;
+ }
Slog.i(TAG, "Backup enabled => " + enable);
@@ -2891,14 +2900,14 @@
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"listAllTransports");
- return mTransportManager.getBoundTransportNames();
+ return mTransportManager.getRegisteredTransportNames();
}
@Override
public ComponentName[] listAllTransportComponents() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"listAllTransportComponents");
- return mTransportManager.getAllTransportComponents();
+ return mTransportManager.getRegisteredTransportComponents();
}
@Override
@@ -3003,10 +3012,18 @@
/** Selects transport {@code transportName} and returns previous selected transport. */
@Override
+ @Deprecated
+ @Nullable
public String selectBackupTransport(String transportName) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BACKUP, "selectBackupTransport");
+ if (!isAllowedByMandatoryBackupTransportPolicy(transportName)) {
+ // Don't change the transport if it is not allowed.
+ Slog.w(TAG, "Failed to select transport - disallowed by device owner policy.");
+ return mTransportManager.getCurrentTransportName();
+ }
+
final long oldId = Binder.clearCallingIdentity();
try {
String previousTransportName = mTransportManager.selectTransport(transportName);
@@ -3021,10 +3038,20 @@
@Override
public void selectBackupTransportAsync(
- ComponentName transportComponent, ISelectBackupTransportCallback listener) {
+ ComponentName transportComponent, @Nullable ISelectBackupTransportCallback listener) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BACKUP, "selectBackupTransportAsync");
-
+ if (!isAllowedByMandatoryBackupTransportPolicy(transportComponent)) {
+ try {
+ if (listener != null) {
+ Slog.w(TAG, "Failed to select transport - disallowed by device owner policy.");
+ listener.onFailure(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "ISelectBackupTransportCallback listener not available");
+ }
+ return;
+ }
final long oldId = Binder.clearCallingIdentity();
try {
String transportString = transportComponent.flattenToShortString();
@@ -3046,10 +3073,12 @@
}
try {
- if (transportName != null) {
- listener.onSuccess(transportName);
- } else {
- listener.onFailure(result);
+ if (listener != null) {
+ if (transportName != null) {
+ listener.onSuccess(transportName);
+ } else {
+ listener.onFailure(result);
+ }
}
} catch (RemoteException e) {
Slog.e(TAG, "ISelectBackupTransportCallback listener not available");
@@ -3060,6 +3089,38 @@
}
}
+ /**
+ * Returns if the specified transport can be set as the current transport without violating the
+ * mandatory backup transport policy.
+ */
+ private boolean isAllowedByMandatoryBackupTransportPolicy(String transportName) {
+ ComponentName mandatoryBackupTransport = mBackupPolicyEnforcer.getMandatoryBackupTransport();
+ if (mandatoryBackupTransport == null) {
+ return true;
+ }
+ final String mandatoryBackupTransportName;
+ try {
+ mandatoryBackupTransportName =
+ mTransportManager.getTransportName(mandatoryBackupTransport);
+ } catch (TransportNotRegisteredException e) {
+ Slog.e(TAG, "mandatory backup transport not registered!");
+ return false;
+ }
+ return TextUtils.equals(mandatoryBackupTransportName, transportName);
+ }
+
+ /**
+ * Returns if the specified transport can be set as the current transport without violating the
+ * mandatory backup transport policy.
+ */
+ private boolean isAllowedByMandatoryBackupTransportPolicy(ComponentName transport) {
+ ComponentName mandatoryBackupTransport = mBackupPolicyEnforcer.getMandatoryBackupTransport();
+ if (mandatoryBackupTransport == null) {
+ return true;
+ }
+ return mandatoryBackupTransport.equals(transport);
+ }
+
private void updateStateForTransport(String newTransportName) {
// Publish the name change
Settings.Secure.putString(mContext.getContentResolver(),
@@ -3079,6 +3140,7 @@
mCurrentToken = 0;
Slog.w(TAG, "Transport " + newTransportName + " not available: current token = 0");
}
+ mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
} else {
Slog.w(TAG, "Transport " + newTransportName + " not registered: current token = 0");
// The named transport isn't registered, so we can't know what its current dataset token
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index a628c9d..540f5a1 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -177,12 +177,15 @@
}
}
+ // IBackupManager binder API
+
/**
* Querying activity state of backup service. Calling this method before initialize yields
* undefined result.
* @param userHandle The user in which the activity state of backup service is queried.
* @return true if the service is active.
*/
+ @Override
public boolean isBackupServiceActive(final int userHandle) {
// TODO: http://b/22388012
if (userHandle == UserHandle.USER_SYSTEM) {
@@ -193,7 +196,6 @@
return false;
}
- // IBackupManager binder API
@Override
public void dataChanged(String packageName) throws RemoteException {
BackupManagerServiceInterface svc = mService;
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index 34b8935..30fd25a 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -16,93 +16,54 @@
package com.android.server.backup;
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
-
import android.annotation.Nullable;
+import android.annotation.WorkerThread;
import android.app.backup.BackupManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.EventLog;
-import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
-import com.android.server.EventLogTags;
+import com.android.internal.util.Preconditions;
+import com.android.server.backup.transport.OnTransportRegisteredListener;
import com.android.server.backup.transport.TransportClient;
import com.android.server.backup.transport.TransportClientManager;
import com.android.server.backup.transport.TransportConnectionListener;
import com.android.server.backup.transport.TransportNotAvailableException;
import com.android.server.backup.transport.TransportNotRegisteredException;
-import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
-/**
- * Handles in-memory bookkeeping of all BackupTransport objects.
- */
+/** Handles in-memory bookkeeping of all BackupTransport objects. */
public class TransportManager {
-
private static final String TAG = "BackupTransportManager";
@VisibleForTesting
public static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
- private static final long REBINDING_TIMEOUT_UNPROVISIONED_MS = 30 * 1000; // 30 sec
- private static final long REBINDING_TIMEOUT_PROVISIONED_MS = 5 * 60 * 1000; // 5 mins
- private static final int REBINDING_TIMEOUT_MSG = 1;
-
private final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
private final Context mContext;
private final PackageManager mPackageManager;
private final Set<ComponentName> mTransportWhitelist;
- private final Handler mHandler;
private final TransportClientManager mTransportClientManager;
-
- /**
- * This listener is called after we bind to any transport. If it returns true, this is a valid
- * transport.
- */
- private TransportBoundListener mTransportBoundListener;
-
private final Object mTransportLock = new Object();
-
- /**
- * We have detected these transports on the device. Unless in exceptional cases, we are also
- * bound to all of these.
- */
- @GuardedBy("mTransportLock")
- private final Map<ComponentName, TransportConnection> mValidTransports = new ArrayMap<>();
-
- /** We are currently bound to these transports. */
- @GuardedBy("mTransportLock")
- private final Map<String, ComponentName> mBoundTransports = new ArrayMap<>();
-
- /** @see #getEligibleTransportComponents() */
- @GuardedBy("mTransportLock")
- private final Set<ComponentName> mEligibleTransports = new ArraySet<>();
+ private OnTransportRegisteredListener mOnTransportRegisteredListener = (c, n) -> {};
/** @see #getRegisteredTransportNames() */
@GuardedBy("mTransportLock")
@@ -110,120 +71,98 @@
new ArrayMap<>();
@GuardedBy("mTransportLock")
+ @Nullable
private volatile String mCurrentTransportName;
- TransportManager(
- Context context,
- Set<ComponentName> whitelist,
- String defaultTransport,
- TransportBoundListener listener,
- Looper looper) {
- this(context, whitelist, defaultTransport, looper);
- mTransportBoundListener = listener;
+ TransportManager(Context context, Set<ComponentName> whitelist, String selectedTransport) {
+ this(context, whitelist, selectedTransport, new TransportClientManager(context));
}
+ @VisibleForTesting
TransportManager(
Context context,
Set<ComponentName> whitelist,
- String defaultTransport,
- Looper looper) {
+ String selectedTransport,
+ TransportClientManager transportClientManager) {
mContext = context;
mPackageManager = context.getPackageManager();
- if (whitelist != null) {
- mTransportWhitelist = whitelist;
- } else {
- mTransportWhitelist = new ArraySet<>();
- }
- mCurrentTransportName = defaultTransport;
- mHandler = new RebindOnTimeoutHandler(looper);
- mTransportClientManager = new TransportClientManager(context);
+ mTransportWhitelist = Preconditions.checkNotNull(whitelist);
+ mCurrentTransportName = selectedTransport;
+ mTransportClientManager = transportClientManager;
}
- public void setTransportBoundListener(TransportBoundListener transportBoundListener) {
- mTransportBoundListener = transportBoundListener;
+ /* Sets a listener to be called whenever a transport is registered. */
+ public void setOnTransportRegisteredListener(OnTransportRegisteredListener listener) {
+ mOnTransportRegisteredListener = listener;
}
+ @WorkerThread
void onPackageAdded(String packageName) {
- // New package added. Bind to all transports it contains.
- synchronized (mTransportLock) {
- log_verbose("Package added. Binding to all transports. " + packageName);
- bindToAllInternal(packageName, null /* all components */);
- }
+ registerTransportsFromPackage(packageName, transportComponent -> true);
}
void onPackageRemoved(String packageName) {
- // Package removed. Remove all its transports from our list. These transports have already
- // been removed from mBoundTransports because onServiceDisconnected would already been
- // called on TransportConnection objects.
synchronized (mTransportLock) {
- Iterator<Map.Entry<ComponentName, TransportConnection>> iter =
- mValidTransports.entrySet().iterator();
- while (iter.hasNext()) {
- Map.Entry<ComponentName, TransportConnection> validTransport = iter.next();
- ComponentName componentName = validTransport.getKey();
- if (componentName.getPackageName().equals(packageName)) {
- TransportConnection transportConnection = validTransport.getValue();
- iter.remove();
- if (transportConnection != null) {
- mContext.unbindService(transportConnection);
- log_verbose("Package removed, removing transport: "
- + componentName.flattenToShortString());
- }
- }
- }
- removeTransportsIfLocked(
- componentName -> packageName.equals(componentName.getPackageName()));
+ mRegisteredTransportsDescriptionMap.keySet().removeIf(fromPackageFilter(packageName));
}
}
- void onPackageChanged(String packageName, String[] components) {
+ @WorkerThread
+ void onPackageChanged(String packageName, String... components) {
synchronized (mTransportLock) {
- // Remove all changed components from mValidTransports. We'll bind to them again
- // and re-add them if still valid.
- Set<ComponentName> transportsToBeRemoved = new ArraySet<>();
- for (String component : components) {
- ComponentName componentName = new ComponentName(packageName, component);
- transportsToBeRemoved.add(componentName);
- TransportConnection removed = mValidTransports.remove(componentName);
- if (removed != null) {
- mContext.unbindService(removed);
- log_verbose("Package changed. Removing transport: " +
- componentName.flattenToShortString());
- }
- }
- removeTransportsIfLocked(transportsToBeRemoved::contains);
- bindToAllInternal(packageName, components);
+ Set<ComponentName> transportComponents =
+ Stream.of(components)
+ .map(component -> new ComponentName(packageName, component))
+ .collect(Collectors.toSet());
+
+ mRegisteredTransportsDescriptionMap.keySet().removeIf(transportComponents::contains);
+ registerTransportsFromPackage(packageName, transportComponents::contains);
}
}
- @GuardedBy("mTransportLock")
- private void removeTransportsIfLocked(Predicate<ComponentName> filter) {
- mEligibleTransports.removeIf(filter);
- mRegisteredTransportsDescriptionMap.keySet().removeIf(filter);
- }
-
- public IBackupTransport getTransportBinder(String transportName) {
+ /**
+ * Returns the {@link ComponentName}s of the registered transports.
+ *
+ * <p>A *registered* transport is a transport that satisfies intent with action
+ * android.backup.TRANSPORT_HOST, returns true for {@link #isTransportTrusted(ComponentName)}
+ * and that we have successfully connected to once.
+ */
+ ComponentName[] getRegisteredTransportComponents() {
synchronized (mTransportLock) {
- ComponentName component = mBoundTransports.get(transportName);
- if (component == null) {
- Slog.w(TAG, "Transport " + transportName + " not bound.");
- return null;
- }
- TransportConnection conn = mValidTransports.get(component);
- if (conn == null) {
- Slog.w(TAG, "Transport " + transportName + " not valid.");
- return null;
- }
- return conn.getBinder();
+ return mRegisteredTransportsDescriptionMap
+ .keySet()
+ .toArray(new ComponentName[mRegisteredTransportsDescriptionMap.size()]);
}
}
- public IBackupTransport getCurrentTransportBinder() {
- return getTransportBinder(mCurrentTransportName);
+ /**
+ * Returns the names of the registered transports.
+ *
+ * @see #getRegisteredTransportComponents()
+ */
+ String[] getRegisteredTransportNames() {
+ synchronized (mTransportLock) {
+ return mRegisteredTransportsDescriptionMap
+ .values()
+ .stream()
+ .map(transportDescription -> transportDescription.name)
+ .toArray(String[]::new);
+ }
+ }
+
+ /** Returns a set with the whitelisted transports. */
+ Set<ComponentName> getTransportWhitelist() {
+ return mTransportWhitelist;
+ }
+
+ @Nullable
+ String getCurrentTransportName() {
+ return mCurrentTransportName;
}
/**
* Returns the transport name associated with {@code transportComponent}.
+ *
* @throws TransportNotRegisteredException if the transport is not registered.
*/
public String getTransportName(ComponentName transportComponent)
@@ -234,7 +173,32 @@
}
/**
- * Retrieve the configuration intent of {@code transportName}.
+ * Retrieves the transport dir name of {@code transportComponent}.
+ *
+ * @throws TransportNotRegisteredException if the transport is not registered.
+ */
+ public String getTransportDirName(ComponentName transportComponent)
+ throws TransportNotRegisteredException {
+ synchronized (mTransportLock) {
+ return getRegisteredTransportDescriptionOrThrowLocked(transportComponent)
+ .transportDirName;
+ }
+ }
+
+ /**
+ * Retrieves the transport dir name of {@code transportName}.
+ *
+ * @throws TransportNotRegisteredException if the transport is not registered.
+ */
+ public String getTransportDirName(String transportName) throws TransportNotRegisteredException {
+ synchronized (mTransportLock) {
+ return getRegisteredTransportDescriptionOrThrowLocked(transportName).transportDirName;
+ }
+ }
+
+ /**
+ * Retrieves the configuration intent of {@code transportName}.
+ *
* @throws TransportNotRegisteredException if the transport is not registered.
*/
@Nullable
@@ -247,7 +211,21 @@
}
/**
- * Retrieve the data management intent of {@code transportName}.
+ * Retrieves the current destination string of {@code transportName}.
+ *
+ * @throws TransportNotRegisteredException if the transport is not registered.
+ */
+ public String getTransportCurrentDestinationString(String transportName)
+ throws TransportNotRegisteredException {
+ synchronized (mTransportLock) {
+ return getRegisteredTransportDescriptionOrThrowLocked(transportName)
+ .currentDestinationString;
+ }
+ }
+
+ /**
+ * Retrieves the data management intent of {@code transportName}.
+ *
* @throws TransportNotRegisteredException if the transport is not registered.
*/
@Nullable
@@ -260,19 +238,8 @@
}
/**
- * Retrieve the current destination string of {@code transportName}.
- * @throws TransportNotRegisteredException if the transport is not registered.
- */
- public String getTransportCurrentDestinationString(String transportName)
- throws TransportNotRegisteredException {
- synchronized (mTransportLock) {
- return getRegisteredTransportDescriptionOrThrowLocked(transportName)
- .currentDestinationString;
- }
- }
-
- /**
- * Retrieve the data management label of {@code transportName}.
+ * Retrieves the data management label of {@code transportName}.
+ *
* @throws TransportNotRegisteredException if the transport is not registered.
*/
@Nullable
@@ -284,54 +251,74 @@
}
}
- /**
- * Retrieve the transport dir name of {@code transportName}.
- * @throws TransportNotRegisteredException if the transport is not registered.
- */
- public String getTransportDirName(String transportName)
- throws TransportNotRegisteredException {
+ /* Returns true if the transport identified by {@code transportName} is registered. */
+ public boolean isTransportRegistered(String transportName) {
synchronized (mTransportLock) {
- return getRegisteredTransportDescriptionOrThrowLocked(transportName)
- .transportDirName;
- }
- }
-
- /**
- * Retrieve the transport dir name of {@code transportComponent}.
- * @throws TransportNotRegisteredException if the transport is not registered.
- */
- public String getTransportDirName(ComponentName transportComponent)
- throws TransportNotRegisteredException {
- synchronized (mTransportLock) {
- return getRegisteredTransportDescriptionOrThrowLocked(transportComponent)
- .transportDirName;
+ return getRegisteredTransportEntryLocked(transportName) != null;
}
}
/**
* Execute {@code transportConsumer} for each registered transport passing the transport name.
* This is called with an internal lock held, ensuring that the transport will remain registered
- * while {@code transportConsumer} is being executed. Don't do heavy operations in
- * {@code transportConsumer}.
+ * while {@code transportConsumer} is being executed. Don't do heavy operations in {@code
+ * transportConsumer}.
*/
public void forEachRegisteredTransport(Consumer<String> transportConsumer) {
synchronized (mTransportLock) {
- for (TransportDescription transportDescription
- : mRegisteredTransportsDescriptionMap.values()) {
+ for (TransportDescription transportDescription :
+ mRegisteredTransportsDescriptionMap.values()) {
transportConsumer.accept(transportDescription.name);
}
}
}
- public String getTransportName(IBackupTransport binder) {
+ /**
+ * Updates given values for the transport already registered and identified with {@param
+ * transportComponent}. If the transport is not registered it will log and return.
+ */
+ public void updateTransportAttributes(
+ ComponentName transportComponent,
+ String name,
+ @Nullable Intent configurationIntent,
+ String currentDestinationString,
+ @Nullable Intent dataManagementIntent,
+ @Nullable String dataManagementLabel) {
synchronized (mTransportLock) {
- for (TransportConnection conn : mValidTransports.values()) {
- if (conn.getBinder() == binder) {
- return conn.getName();
- }
+ TransportDescription description =
+ mRegisteredTransportsDescriptionMap.get(transportComponent);
+ if (description == null) {
+ Slog.e(TAG, "Transport " + name + " not registered tried to change description");
+ return;
}
+ description.name = name;
+ description.configurationIntent = configurationIntent;
+ description.currentDestinationString = currentDestinationString;
+ description.dataManagementIntent = dataManagementIntent;
+ description.dataManagementLabel = dataManagementLabel;
+ Slog.d(TAG, "Transport " + name + " updated its attributes");
}
- return null;
+ }
+
+ @GuardedBy("mTransportLock")
+ private TransportDescription getRegisteredTransportDescriptionOrThrowLocked(
+ ComponentName transportComponent) throws TransportNotRegisteredException {
+ TransportDescription description =
+ mRegisteredTransportsDescriptionMap.get(transportComponent);
+ if (description == null) {
+ throw new TransportNotRegisteredException(transportComponent);
+ }
+ return description;
+ }
+
+ @GuardedBy("mTransportLock")
+ private TransportDescription getRegisteredTransportDescriptionOrThrowLocked(
+ String transportName) throws TransportNotRegisteredException {
+ TransportDescription description = getRegisteredTransportDescriptionLocked(transportName);
+ if (description == null) {
+ throw new TransportNotRegisteredException(transportName);
+ }
+ return description;
}
@GuardedBy("mTransportLock")
@@ -351,21 +338,11 @@
}
@GuardedBy("mTransportLock")
- private TransportDescription getRegisteredTransportDescriptionOrThrowLocked(
- String transportName) throws TransportNotRegisteredException {
- TransportDescription description = getRegisteredTransportDescriptionLocked(transportName);
- if (description == null) {
- throw new TransportNotRegisteredException(transportName);
- }
- return description;
- }
-
- @GuardedBy("mTransportLock")
@Nullable
private Map.Entry<ComponentName, TransportDescription> getRegisteredTransportEntryLocked(
String transportName) {
- for (Map.Entry<ComponentName, TransportDescription> entry
- : mRegisteredTransportsDescriptionMap.entrySet()) {
+ for (Map.Entry<ComponentName, TransportDescription> entry :
+ mRegisteredTransportsDescriptionMap.entrySet()) {
TransportDescription description = entry.getValue();
if (transportName.equals(description.name)) {
return entry;
@@ -374,17 +351,16 @@
return null;
}
- @GuardedBy("mTransportLock")
- private TransportDescription getRegisteredTransportDescriptionOrThrowLocked(
- ComponentName transportComponent) throws TransportNotRegisteredException {
- TransportDescription description =
- mRegisteredTransportsDescriptionMap.get(transportComponent);
- if (description == null) {
- throw new TransportNotRegisteredException(transportComponent);
- }
- return description;
- }
-
+ /**
+ * Returns a {@link TransportClient} for {@code transportName} or {@code null} if not
+ * registered.
+ *
+ * @param transportName The name of the transport.
+ * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
+ * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
+ * details.
+ * @return A {@link TransportClient} or null if not registered.
+ */
@Nullable
public TransportClient getTransportClient(String transportName, String caller) {
try {
@@ -395,6 +371,16 @@
}
}
+ /**
+ * Returns a {@link TransportClient} for {@code transportName} or throws if not registered.
+ *
+ * @param transportName The name of the transport.
+ * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
+ * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
+ * details.
+ * @return A {@link TransportClient}.
+ * @throws TransportNotRegisteredException if the transport is not registered.
+ */
public TransportClient getTransportClientOrThrow(String transportName, String caller)
throws TransportNotRegisteredException {
synchronized (mTransportLock) {
@@ -406,19 +392,14 @@
}
}
- public boolean isTransportRegistered(String transportName) {
- synchronized (mTransportLock) {
- return getRegisteredTransportEntryLocked(transportName) != null;
- }
- }
-
/**
- * Returns a {@link TransportClient} for the current transport or null if not found.
+ * Returns a {@link TransportClient} for the current transport or {@code null} if not
+ * registered.
*
* @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
* {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
* details.
- * @return A {@link TransportClient} or null if not found.
+ * @return A {@link TransportClient} or null if not registered.
*/
@Nullable
public TransportClient getCurrentTransportClient(String caller) {
@@ -455,130 +436,88 @@
mTransportClientManager.disposeOfTransportClient(transportClient, caller);
}
- String[] getBoundTransportNames() {
- synchronized (mTransportLock) {
- return mBoundTransports.keySet().toArray(new String[mBoundTransports.size()]);
- }
- }
-
- ComponentName[] getAllTransportComponents() {
- synchronized (mTransportLock) {
- return mValidTransports.keySet().toArray(new ComponentName[mValidTransports.size()]);
- }
- }
-
/**
- * An *eligible* transport is a service component that satisfies intent with action
- * android.backup.TRANSPORT_HOST and returns true for
- * {@link #isTransportTrusted(ComponentName)}. It may be registered or not registered.
- * This method returns the {@link ComponentName}s of those transports.
- */
- ComponentName[] getEligibleTransportComponents() {
- synchronized (mTransportLock) {
- return mEligibleTransports.toArray(new ComponentName[mEligibleTransports.size()]);
- }
- }
-
- Set<ComponentName> getTransportWhitelist() {
- return mTransportWhitelist;
- }
-
- /**
- * A *registered* transport is an eligible transport that has been successfully connected and
- * that returned true for method
- * {@link TransportBoundListener#onTransportBound(IBackupTransport)} of TransportBoundListener
- * provided in the constructor. This method returns the names of the registered transports.
- */
- String[] getRegisteredTransportNames() {
- synchronized (mTransportLock) {
- return mRegisteredTransportsDescriptionMap.values().stream()
- .map(transportDescription -> transportDescription.name)
- .toArray(String[]::new);
- }
- }
-
- /**
- * Updates given values for the transport already registered and identified with
- * {@param transportComponent}. If the transport is not registered it will log and return.
- */
- public void updateTransportAttributes(
- ComponentName transportComponent,
- String name,
- @Nullable Intent configurationIntent,
- String currentDestinationString,
- @Nullable Intent dataManagementIntent,
- @Nullable String dataManagementLabel) {
- synchronized (mTransportLock) {
- TransportDescription description =
- mRegisteredTransportsDescriptionMap.get(transportComponent);
- if (description == null) {
- Slog.e(TAG, "Transport " + name + " not registered tried to change description");
- return;
- }
- description.name = name;
- description.configurationIntent = configurationIntent;
- description.currentDestinationString = currentDestinationString;
- description.dataManagementIntent = dataManagementIntent;
- description.dataManagementLabel = dataManagementLabel;
- Slog.d(TAG, "Transport " + name + " updated its attributes");
- }
- }
-
- @Nullable
- String getCurrentTransportName() {
- return mCurrentTransportName;
- }
-
- // This is for mocking, Mockito can't mock if package-protected and in the same package but
- // different class loaders. Checked with the debugger and class loaders are different
- // See https://github.com/mockito/mockito/issues/796
- @VisibleForTesting(visibility = PACKAGE)
- public void registerAllTransports() {
- bindToAllInternal(null /* all packages */, null /* all components */);
- }
-
- /**
- * Bind to all transports belonging to the given package and the given component list.
- * null acts a wildcard.
+ * Sets {@code transportName} as selected transport and returns previously selected transport
+ * name. If there was no previous transport it returns null.
*
- * If packageName is null, bind to all transports in all packages.
- * If components is null, bind to all transports in the given package.
+ * <p>You should NOT call this method in new code. This won't make any checks against {@code
+ * transportName}, putting any operation at risk of a {@link TransportNotRegisteredException} or
+ * another error at the time it's being executed.
+ *
+ * <p>{@link Deprecated} as public, this method can be used as private.
*/
- private void bindToAllInternal(String packageName, String[] components) {
- PackageInfo pkgInfo = null;
- if (packageName != null) {
+ @Deprecated
+ @Nullable
+ String selectTransport(String transportName) {
+ synchronized (mTransportLock) {
+ String prevTransport = mCurrentTransportName;
+ mCurrentTransportName = transportName;
+ return prevTransport;
+ }
+ }
+
+ /**
+ * Tries to register the transport if not registered. If successful also selects the transport.
+ *
+ * @param transportComponent Host of the transport.
+ * @return One of {@link BackupManager#SUCCESS}, {@link BackupManager#ERROR_TRANSPORT_INVALID}
+ * or {@link BackupManager#ERROR_TRANSPORT_UNAVAILABLE}.
+ */
+ @WorkerThread
+ public int registerAndSelectTransport(ComponentName transportComponent) {
+ synchronized (mTransportLock) {
+ if (!mRegisteredTransportsDescriptionMap.containsKey(transportComponent)) {
+ int result = registerTransport(transportComponent);
+ if (result != BackupManager.SUCCESS) {
+ return result;
+ }
+ }
+
try {
- pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.w(TAG, "Package not found: " + packageName);
- return;
+ selectTransport(getTransportName(transportComponent));
+ return BackupManager.SUCCESS;
+ } catch (TransportNotRegisteredException e) {
+ // Shouldn't happen because we are holding the lock
+ Slog.wtf(TAG, "Transport unexpectedly not registered");
+ return BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
}
}
+ }
- Intent intent = new Intent(mTransportServiceIntent);
- if (packageName != null) {
- intent.setPackage(packageName);
+ @WorkerThread
+ public void registerTransports() {
+ registerTransportsForIntent(mTransportServiceIntent, transportComponent -> true);
+ }
+
+ @WorkerThread
+ private void registerTransportsFromPackage(
+ String packageName, Predicate<ComponentName> transportComponentFilter) {
+ try {
+ mPackageManager.getPackageInfo(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Trying to register transports from package not found " + packageName);
+ return;
}
- List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
- intent, 0, UserHandle.USER_SYSTEM);
- if (hosts != null) {
+ registerTransportsForIntent(
+ new Intent(mTransportServiceIntent).setPackage(packageName),
+ transportComponentFilter.and(fromPackageFilter(packageName)));
+ }
+
+ @WorkerThread
+ private void registerTransportsForIntent(
+ Intent intent, Predicate<ComponentName> transportComponentFilter) {
+ List<ResolveInfo> hosts =
+ mPackageManager.queryIntentServicesAsUser(intent, 0, UserHandle.USER_SYSTEM);
+ if (hosts == null) {
+ return;
+ }
+ synchronized (mTransportLock) {
for (ResolveInfo host : hosts) {
- final ComponentName infoComponentName = getComponentName(host.serviceInfo);
- boolean shouldBind = false;
- if (components != null && packageName != null) {
- for (String component : components) {
- ComponentName cn = new ComponentName(pkgInfo.packageName, component);
- if (infoComponentName.equals(cn)) {
- shouldBind = true;
- break;
- }
- }
- } else {
- shouldBind = true;
- }
- if (shouldBind && isTransportTrusted(infoComponentName)) {
- tryBindTransport(infoComponentName);
+ ComponentName transportComponent = host.serviceInfo.getComponentName();
+ if (transportComponentFilter.test(transportComponent)
+ && isTransportTrusted(transportComponent)) {
+ registerTransport(transportComponent);
}
}
}
@@ -605,64 +544,6 @@
return true;
}
- private void tryBindTransport(ComponentName transportComponentName) {
- Slog.d(TAG, "Binding to transport: " + transportComponentName.flattenToShortString());
- // TODO: b/22388012 (Multi user backup and restore)
- TransportConnection connection = new TransportConnection(transportComponentName);
- synchronized (mTransportLock) {
- mEligibleTransports.add(transportComponentName);
- }
- if (bindToTransport(transportComponentName, connection)) {
- synchronized (mTransportLock) {
- mValidTransports.put(transportComponentName, connection);
- }
- } else {
- Slog.w(TAG, "Couldn't bind to transport " + transportComponentName);
- }
- }
-
- private boolean bindToTransport(ComponentName componentName, ServiceConnection connection) {
- Intent intent = new Intent(mTransportServiceIntent)
- .setComponent(componentName);
- return mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
- createSystemUserHandle());
- }
-
- String selectTransport(String transportName) {
- synchronized (mTransportLock) {
- String prevTransport = mCurrentTransportName;
- mCurrentTransportName = transportName;
- return prevTransport;
- }
- }
-
- /**
- * Tries to register the transport if not registered. If successful also selects the transport.
- *
- * @param transportComponent Host of the transport.
- * @return One of {@link BackupManager#SUCCESS}, {@link BackupManager#ERROR_TRANSPORT_INVALID}
- * or {@link BackupManager#ERROR_TRANSPORT_UNAVAILABLE}.
- */
- public int registerAndSelectTransport(ComponentName transportComponent) {
- synchronized (mTransportLock) {
- if (!mRegisteredTransportsDescriptionMap.containsKey(transportComponent)) {
- int result = registerTransport(transportComponent);
- if (result != BackupManager.SUCCESS) {
- return result;
- }
- }
-
- try {
- selectTransport(getTransportName(transportComponent));
- return BackupManager.SUCCESS;
- } catch (TransportNotRegisteredException e) {
- // Shouldn't happen because we are holding the lock
- Slog.wtf(TAG, "Transport unexpectedly not registered");
- return BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
- }
- }
- }
-
/**
* Tries to register transport represented by {@code transportComponent}.
*
@@ -670,7 +551,12 @@
* @return One of {@link BackupManager#SUCCESS}, {@link BackupManager#ERROR_TRANSPORT_INVALID}
* or {@link BackupManager#ERROR_TRANSPORT_UNAVAILABLE}.
*/
+ @WorkerThread
private int registerTransport(ComponentName transportComponent) {
+ if (!isTransportTrusted(transportComponent)) {
+ return BackupManager.ERROR_TRANSPORT_INVALID;
+ }
+
String transportString = transportComponent.flattenToShortString();
String callerLogString = "TransportManager.registerTransport()";
@@ -686,29 +572,21 @@
return BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
}
- EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transportString, 1);
-
int result;
- if (isTransportValid(transport)) {
- try {
- registerTransport(transportComponent, transport);
- // If registerTransport() hasn't thrown...
- result = BackupManager.SUCCESS;
- } catch (RemoteException e) {
- Slog.e(TAG, "Transport " + transportString + " died while registering");
- result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
- }
- } else {
- Slog.w(TAG, "Can't register invalid transport " + transportString);
- result = BackupManager.ERROR_TRANSPORT_INVALID;
+ try {
+ String transportName = transport.name();
+ String transportDirName = transport.transportDirName();
+ registerTransport(transportComponent, transport);
+ // If registerTransport() hasn't thrown...
+ Slog.d(TAG, "Transport " + transportString + " registered");
+ mOnTransportRegisteredListener.onTransportRegistered(transportName, transportDirName);
+ result = BackupManager.SUCCESS;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Transport " + transportString + " died while registering");
+ result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
}
mTransportClientManager.disposeOfTransportClient(transportClient, callerLogString);
- if (result == BackupManager.SUCCESS) {
- Slog.d(TAG, "Transport " + transportString + " registered");
- } else {
- EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transportString, 0);
- }
return result;
}
@@ -717,204 +595,20 @@
throws RemoteException {
synchronized (mTransportLock) {
String name = transport.name();
- TransportDescription description = new TransportDescription(
- name,
- transport.transportDirName(),
- transport.configurationIntent(),
- transport.currentDestinationString(),
- transport.dataManagementIntent(),
- transport.dataManagementLabel());
+ TransportDescription description =
+ new TransportDescription(
+ name,
+ transport.transportDirName(),
+ transport.configurationIntent(),
+ transport.currentDestinationString(),
+ transport.dataManagementIntent(),
+ transport.dataManagementLabel());
mRegisteredTransportsDescriptionMap.put(transportComponent, description);
}
}
- private boolean isTransportValid(IBackupTransport transport) {
- if (mTransportBoundListener == null) {
- Slog.w(TAG, "setTransportBoundListener() not called, assuming transport invalid");
- return false;
- }
- return mTransportBoundListener.onTransportBound(transport);
- }
-
- private class TransportConnection implements ServiceConnection {
-
- // Hold mTransportLock to access these fields so as to provide a consistent view of them.
- private volatile IBackupTransport mBinder;
- private volatile String mTransportName;
-
- private final ComponentName mTransportComponent;
-
- private TransportConnection(ComponentName transportComponent) {
- mTransportComponent = transportComponent;
- }
-
- @Override
- public void onServiceConnected(ComponentName component, IBinder binder) {
- synchronized (mTransportLock) {
- mBinder = IBackupTransport.Stub.asInterface(binder);
- boolean success = false;
-
- EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE,
- component.flattenToShortString(), 1);
-
- try {
- mTransportName = mBinder.name();
- // BackupManager requests some fields from the transport. If they are
- // invalid, throw away this transport.
- if (isTransportValid(mBinder)) {
- // We're now using the always-bound connection to do the registration but
- // when we remove the always-bound code this will be in the first binding
- // TODO: Move registration to first binding
- registerTransport(component, mBinder);
- // If registerTransport() hasn't thrown...
- success = true;
- }
- } catch (RemoteException e) {
- success = false;
- Slog.e(TAG, "Couldn't get transport name.", e);
- } finally {
- // we need to intern() the String of the component, so that we can use it with
- // Handler's removeMessages(), which uses == operator to compare the tokens
- String componentShortString = component.flattenToShortString().intern();
- if (success) {
- Slog.d(TAG, "Bound to transport: " + componentShortString);
- mBoundTransports.put(mTransportName, component);
- // cancel rebinding on timeout for this component as we've already connected
- mHandler.removeMessages(REBINDING_TIMEOUT_MSG, componentShortString);
- } else {
- Slog.w(TAG, "Bound to transport " + componentShortString +
- " but it is invalid");
- EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE,
- componentShortString, 0);
- mContext.unbindService(this);
- mValidTransports.remove(component);
- mEligibleTransports.remove(component);
- mBinder = null;
- }
- }
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName component) {
- synchronized (mTransportLock) {
- mBinder = null;
- mBoundTransports.remove(mTransportName);
- }
- String componentShortString = component.flattenToShortString();
- EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, componentShortString, 0);
- Slog.w(TAG, "Disconnected from transport " + componentShortString);
- scheduleRebindTimeout(component);
- }
-
- /**
- * We'll attempt to explicitly rebind to a transport if it hasn't happened automatically
- * for a few minutes after the binding went away.
- */
- private void scheduleRebindTimeout(ComponentName component) {
- // we need to intern() the String of the component, so that we can use it with Handler's
- // removeMessages(), which uses == operator to compare the tokens
- final String componentShortString = component.flattenToShortString().intern();
- final long rebindTimeout = getRebindTimeout();
- mHandler.removeMessages(REBINDING_TIMEOUT_MSG, componentShortString);
- Message msg = mHandler.obtainMessage(REBINDING_TIMEOUT_MSG);
- msg.obj = componentShortString;
- mHandler.sendMessageDelayed(msg, rebindTimeout);
- Slog.d(TAG, "Scheduled explicit rebinding for " + componentShortString + " in "
- + rebindTimeout + "ms");
- }
-
- // Intentionally not synchronized -- the variable is volatile and changes to its value
- // are inside synchronized blocks, providing a memory sync barrier; and this method
- // does not touch any other state protected by that lock.
- private IBackupTransport getBinder() {
- return mBinder;
- }
-
- // Intentionally not synchronized; same as getBinder()
- private String getName() {
- return mTransportName;
- }
-
- // Intentionally not synchronized; same as getBinder()
- private void bindIfUnbound() {
- if (mBinder == null) {
- Slog.d(TAG,
- "Rebinding to transport " + mTransportComponent.flattenToShortString());
- bindToTransport(mTransportComponent, this);
- }
- }
-
- private long getRebindTimeout() {
- final boolean isDeviceProvisioned = Settings.Global.getInt(
- mContext.getContentResolver(),
- Settings.Global.DEVICE_PROVISIONED, 0) != 0;
- return isDeviceProvisioned
- ? REBINDING_TIMEOUT_PROVISIONED_MS
- : REBINDING_TIMEOUT_UNPROVISIONED_MS;
- }
- }
-
- public interface TransportBoundListener {
- /** Should return true if this is a valid transport. */
- boolean onTransportBound(IBackupTransport binder);
- }
-
- private class RebindOnTimeoutHandler extends Handler {
-
- RebindOnTimeoutHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == REBINDING_TIMEOUT_MSG) {
- String componentShortString = (String) msg.obj;
- ComponentName transportComponent =
- ComponentName.unflattenFromString(componentShortString);
- synchronized (mTransportLock) {
- if (mBoundTransports.containsValue(transportComponent)) {
- Slog.d(TAG, "Explicit rebinding timeout passed, but already bound to "
- + componentShortString + " so not attempting to rebind");
- return;
- }
- Slog.d(TAG, "Explicit rebinding timeout passed, attempting rebinding to: "
- + componentShortString);
- // unbind the existing (broken) connection
- TransportConnection conn = mValidTransports.get(transportComponent);
- if (conn != null) {
- mContext.unbindService(conn);
- Slog.d(TAG, "Unbinding the existing (broken) connection to transport: "
- + componentShortString);
- }
- }
- // rebind to transport
- tryBindTransport(transportComponent);
- } else {
- Slog.e(TAG, "Unknown message sent to RebindOnTimeoutHandler, msg.what: "
- + msg.what);
- }
- }
- }
-
- private static void log_verbose(String message) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Slog.v(TAG, message);
- }
- }
-
- // These only exists to make it testable with Robolectric, which is not updated to API level 24
- // yet.
- // TODO: Get rid of this once Robolectric is updated.
- private static ComponentName getComponentName(ServiceInfo serviceInfo) {
- return new ComponentName(serviceInfo.packageName, serviceInfo.name);
- }
-
- // These only exists to make it testable with Robolectric, which is not updated to API level 24
- // yet.
- // TODO: Get rid of this once Robolectric is updated.
- public static UserHandle createSystemUserHandle() {
- return new UserHandle(UserHandle.USER_SYSTEM);
+ private static Predicate<ComponentName> fromPackageFilter(String packageName) {
+ return transportComponent -> packageName.equals(transportComponent.getPackageName());
}
private static class TransportDescription {
diff --git a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
index c232241..cc3af8c 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
@@ -907,7 +907,15 @@
backupData = ParcelFileDescriptor.open(mBackupDataName,
ParcelFileDescriptor.MODE_READ_ONLY);
backupManagerService.addBackupTrace("sending data to transport");
- int flags = mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0;
+
+ int userInitiatedFlag =
+ mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0;
+ int incrementalFlag =
+ mSavedStateName.length() == 0
+ ? BackupTransport.FLAG_NON_INCREMENTAL
+ : BackupTransport.FLAG_INCREMENTAL;
+ int flags = userInitiatedFlag | incrementalFlag;
+
mStatus = transport.performBackup(mCurrentPackage, backupData, flags);
}
diff --git a/services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java b/services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java
new file mode 100644
index 0000000..391ec2d
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.transport;
+
+import com.android.server.backup.TransportManager;
+
+/**
+ * Listener called when a transport is registered with the {@link TransportManager}. Can be set
+ * using {@link TransportManager#setOnTransportRegisteredListener(OnTransportRegisteredListener)}.
+ */
+@FunctionalInterface
+public interface OnTransportRegisteredListener {
+ /**
+ * Called when a transport is successfully registered.
+ * @param transportName The name of the transport.
+ * @param transportDirName The dir name of the transport.
+ */
+ public void onTransportRegistered(String transportName, String transportDirName);
+}
diff --git a/services/backup/java/com/android/server/backup/transport/TransportClient.java b/services/backup/java/com/android/server/backup/transport/TransportClient.java
index 7bd9111..399f338 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportClient.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportClient.java
@@ -29,12 +29,14 @@
import android.os.Looper;
import android.os.UserHandle;
import android.util.ArrayMap;
+import android.util.EventLog;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
import com.android.internal.util.Preconditions;
+import com.android.server.EventLogTags;
import com.android.server.backup.TransportManager;
import java.lang.annotation.Retention;
@@ -236,7 +238,7 @@
mBindIntent,
mConnection,
Context.BIND_AUTO_CREATE,
- TransportManager.createSystemUserHandle());
+ UserHandle.SYSTEM);
if (hasBound) {
// We don't need to set a time-out because we are guaranteed to get a call
// back in ServiceConnection, either an onServiceConnected() or
@@ -419,10 +421,45 @@
@GuardedBy("mStateLock")
private void setStateLocked(@State int state, @Nullable IBackupTransport transport) {
log(Log.VERBOSE, "State: " + stateToString(mState) + " => " + stateToString(state));
+ onStateTransition(mState, state);
mState = state;
mTransport = transport;
}
+ private void onStateTransition(int oldState, int newState) {
+ String transport = mTransportComponent.flattenToShortString();
+ int bound = transitionThroughState(oldState, newState, State.BOUND_AND_CONNECTING);
+ int connected = transitionThroughState(oldState, newState, State.CONNECTED);
+ if (bound != Transition.NO_TRANSITION) {
+ int value = (bound == Transition.UP) ? 1 : 0; // 1 is bound, 0 is not bound
+ EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transport, value);
+ }
+ if (connected != Transition.NO_TRANSITION) {
+ int value = (connected == Transition.UP) ? 1 : 0; // 1 is connected, 0 is not connected
+ EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_CONNECTION, transport, value);
+ }
+ }
+
+ /**
+ * Returns:
+ *
+ * <ul>
+ * <li>{@link Transition#UP}, if oldState < stateReference <= newState
+ * <li>{@link Transition#DOWN}, if oldState >= stateReference > newState
+ * <li>{@link Transition#NO_TRANSITION}, otherwise
+ */
+ @Transition
+ private int transitionThroughState(
+ @State int oldState, @State int newState, @State int stateReference) {
+ if (oldState < stateReference && stateReference <= newState) {
+ return Transition.UP;
+ }
+ if (oldState >= stateReference && stateReference > newState) {
+ return Transition.DOWN;
+ }
+ return Transition.NO_TRANSITION;
+ }
+
@GuardedBy("mStateLock")
private void checkStateIntegrityLocked() {
switch (mState) {
@@ -481,6 +518,14 @@
// CharSequence time = DateFormat.format("yyyy-MM-dd HH:mm:ss", System.currentTimeMillis());
}
+ @IntDef({Transition.DOWN, Transition.NO_TRANSITION, Transition.UP})
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface Transition {
+ int DOWN = -1;
+ int NO_TRANSITION = 0;
+ int UP = 1;
+ }
+
@IntDef({State.UNUSABLE, State.IDLE, State.BOUND_AND_CONNECTING, State.CONNECTED})
@Retention(RetentionPolicy.SOURCE)
private @interface State {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 9fb2681..3369458 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -16,6 +16,7 @@
":installd_aidl",
":storaged_aidl",
":vold_aidl",
+ ":mediaupdateservice_aidl",
"java/com/android/server/EventLogTags.logtags",
"java/com/android/server/am/EventLogTags.logtags",
],
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index a024d5a..15c0f3c 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -89,10 +89,10 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.LocalLog;
import com.android.server.ForceAppStandbyTracker.Listener;
-import com.android.server.LocalServices;
/**
* Alarm manager implementaion.
@@ -152,6 +152,8 @@
private long mLastTickSet;
private long mLastTickIssued; // elapsed
private long mLastTickReceived;
+ private long mLastTickAdded;
+ private long mLastTickRemoved;
int mBroadcastRefCount = 0;
PowerManager.WakeLock mWakeLock;
boolean mLastWakeLockUnimportantForLogging;
@@ -431,6 +433,9 @@
end = seed.maxWhenElapsed;
flags = seed.flags;
alarms.add(seed);
+ if (seed.operation == mTimeTickSender) {
+ mLastTickAdded = System.currentTimeMillis();
+ }
}
int size() {
@@ -453,6 +458,9 @@
index = 0 - index - 1;
}
alarms.add(index, alarm);
+ if (alarm.operation == mTimeTickSender) {
+ mLastTickAdded = System.currentTimeMillis();
+ }
if (DEBUG_BATCH) {
Slog.v(TAG, "Adding " + alarm + " to " + this);
}
@@ -484,6 +492,9 @@
if (alarm.alarmClock != null) {
mNextAlarmClockMayChange = true;
}
+ if (alarm.operation == mTimeTickSender) {
+ mLastTickRemoved = System.currentTimeMillis();
+ }
} else {
if (alarm.whenElapsed > newStart) {
newStart = alarm.whenElapsed;
@@ -700,6 +711,39 @@
}
return -1;
}
+ /** @return total count of the alarms in a set of alarm batches. */
+ static int getAlarmCount(ArrayList<Batch> batches) {
+ int ret = 0;
+
+ final int size = batches.size();
+ for (int i = 0; i < size; i++) {
+ ret += batches.get(i).size();
+ }
+ return ret;
+ }
+
+ boolean haveAlarmsTimeTickAlarm(ArrayList<Alarm> alarms) {
+ if (alarms.size() == 0) {
+ return false;
+ }
+ final int batchSize = alarms.size();
+ for (int j = 0; j < batchSize; j++) {
+ if (alarms.get(j).operation == mTimeTickSender) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean haveBatchesTimeTickAlarm(ArrayList<Batch> batches) {
+ final int numBatches = batches.size();
+ for (int i = 0; i < numBatches; i++) {
+ if (haveAlarmsTimeTickAlarm(batches.get(i).alarms)) {
+ return true;
+ }
+ }
+ return false;
+ }
// The RTC clock has moved arbitrarily, so we need to recalculate all the batching
void rebatchAllAlarms() {
@@ -709,6 +753,11 @@
}
void rebatchAllAlarmsLocked(boolean doValidate) {
+ final int oldCount =
+ getAlarmCount(mAlarmBatches) + ArrayUtils.size(mPendingWhileIdleAlarms);
+ final boolean oldHasTick = haveBatchesTimeTickAlarm(mAlarmBatches)
+ || haveAlarmsTimeTickAlarm(mPendingWhileIdleAlarms);
+
ArrayList<Batch> oldSet = (ArrayList<Batch>) mAlarmBatches.clone();
mAlarmBatches.clear();
Alarm oldPendingIdleUntil = mPendingIdleUntil;
@@ -729,6 +778,18 @@
restorePendingWhileIdleAlarmsLocked();
}
}
+ final int newCount =
+ getAlarmCount(mAlarmBatches) + ArrayUtils.size(mPendingWhileIdleAlarms);
+ final boolean newHasTick = haveBatchesTimeTickAlarm(mAlarmBatches)
+ || haveAlarmsTimeTickAlarm(mPendingWhileIdleAlarms);
+
+ if (oldCount != newCount) {
+ Slog.wtf(TAG, "Rebatching: total count changed from " + oldCount + " to " + newCount);
+ }
+ if (oldHasTick != newHasTick) {
+ Slog.wtf(TAG, "Rebatching: hasTick changed from " + oldHasTick + " to " + newHasTick);
+ }
+
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
}
@@ -1162,12 +1223,12 @@
// ignored; both services live in system_server
}
publishBinderService(Context.ALARM_SERVICE, mService);
- mForceAppStandbyTracker.start();
}
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ mForceAppStandbyTracker.start();
mConstants.start(getContext().getContentResolver());
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
mLocalDeviceIdleController
@@ -1603,7 +1664,7 @@
final long nowRTC = System.currentTimeMillis();
final long nowELAPSED = SystemClock.elapsedRealtime();
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
pw.print(" nowRTC="); pw.print(nowRTC);
pw.print("="); pw.print(sdf.format(new Date(nowRTC)));
@@ -1613,10 +1674,11 @@
pw.print("="); pw.println(sdf.format(new Date(mLastTimeChangeClockTime)));
pw.print(" mLastTimeChangeRealtime="); pw.println(mLastTimeChangeRealtime);
pw.print(" mLastTickIssued=");
- TimeUtils.formatDuration(mLastTickIssued - nowELAPSED, pw);
- pw.println();
+ pw.println(sdf.format(new Date(nowRTC - (nowELAPSED - mLastTickIssued))));
pw.print(" mLastTickReceived="); pw.println(sdf.format(new Date(mLastTickReceived)));
pw.print(" mLastTickSet="); pw.println(sdf.format(new Date(mLastTickSet)));
+ pw.print(" mLastTickAdded="); pw.println(sdf.format(new Date(mLastTickAdded)));
+ pw.print(" mLastTickRemoved="); pw.println(sdf.format(new Date(mLastTickRemoved)));
pw.println();
if (!mInteractive) {
pw.print(" Time since non-interactive: ");
@@ -2400,6 +2462,10 @@
}
void removeLocked(final int uid) {
+ if (uid == Process.SYSTEM_UID) {
+ Slog.wtf(TAG, "removeLocked: Shouldn't for UID=" + uid);
+ return;
+ }
boolean didRemove = false;
final Predicate<Alarm> whichAlarms = (Alarm a) -> a.uid == uid;
for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
@@ -2448,6 +2514,7 @@
boolean didRemove = false;
final Predicate<Alarm> whichAlarms = (Alarm a) -> a.matches(packageName);
+ final boolean oldHasTick = haveBatchesTimeTickAlarm(mAlarmBatches);
for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
Batch b = mAlarmBatches.get(i);
didRemove |= b.remove(whichAlarms);
@@ -2455,6 +2522,11 @@
mAlarmBatches.remove(i);
}
}
+ final boolean newHasTick = haveBatchesTimeTickAlarm(mAlarmBatches);
+ if (oldHasTick != newHasTick) {
+ Slog.wtf(TAG, "removeLocked: hasTick changed from " + oldHasTick + " to " + newHasTick);
+ }
+
for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
final Alarm a = mPendingWhileIdleAlarms.get(i);
if (a.matches(packageName)) {
@@ -2484,6 +2556,10 @@
}
void removeForStoppedLocked(final int uid) {
+ if (uid == Process.SYSTEM_UID) {
+ Slog.wtf(TAG, "removeForStoppedLocked: Shouldn't for UID=" + uid);
+ return;
+ }
boolean didRemove = false;
final Predicate<Alarm> whichAlarms = (Alarm a) -> {
try {
@@ -2524,6 +2600,10 @@
}
void removeUserLocked(int userHandle) {
+ if (userHandle == UserHandle.USER_SYSTEM) {
+ Slog.wtf(TAG, "removeForStoppedLocked: Shouldn't for user=" + userHandle);
+ return;
+ }
boolean didRemove = false;
final Predicate<Alarm> whichAlarms =
(Alarm a) -> UserHandle.getUserId(a.creatorUid) == userHandle;
@@ -3688,6 +3768,9 @@
mDeliveryTracker, mHandler, null,
allowWhileIdle ? mIdleOptions : null);
} catch (PendingIntent.CanceledException e) {
+ if (alarm.operation == mTimeTickSender) {
+ Slog.wtf(TAG, "mTimeTickSender canceled");
+ }
if (alarm.repeatInterval > 0) {
// This IntentSender is no longer valid, but this
// is a repeating alarm, so toss it
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index d9713a5..337406d 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -60,6 +60,7 @@
import android.provider.Settings.SettingNotFoundException;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.util.DumpUtils;
import com.android.server.pm.UserRestrictionsUtils;
@@ -415,9 +416,14 @@
int systemUiUid = -1;
try {
- systemUiUid = mContext.getPackageManager()
- .getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY,
- UserHandle.USER_SYSTEM);
+ // Check if device is configured with no home screen, which implies no SystemUI.
+ boolean noHome = mContext.getResources().getBoolean(R.bool.config_noHomeScreen);
+ if (!noHome) {
+ systemUiUid = mContext.getPackageManager()
+ .getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY,
+ UserHandle.USER_SYSTEM);
+ }
+ Slog.d(TAG, "Detected SystemUiUid: " + Integer.toString(systemUiUid));
} catch (PackageManager.NameNotFoundException e) {
// Some platforms, such as wearables do not have a system ui.
Slog.w(TAG, "Unable to resolve SystemUI's UID.", e);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 86b0164..9574545 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -130,6 +130,8 @@
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.DataConnectionStats;
+import com.android.server.connectivity.DnsManager;
+import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.connectivity.KeepaliveTracker;
import com.android.server.connectivity.LingerMonitor;
@@ -225,7 +227,11 @@
@GuardedBy("mVpns")
private final SparseArray<Vpn> mVpns = new SparseArray<Vpn>();
+ // TODO: investigate if mLockdownEnabled can be removed and replaced everywhere by
+ // a direct call to LockdownVpnTracker.isEnabled().
+ @GuardedBy("mVpns")
private boolean mLockdownEnabled;
+ @GuardedBy("mVpns")
private LockdownVpnTracker mLockdownTracker;
final private Context mContext;
@@ -233,8 +239,6 @@
// 0 is full bad, 100 is full good
private int mDefaultInetConditionPublished = 0;
- private int mNumDnsEntries;
-
private boolean mTestMode;
private static ConnectivityService sServiceInstance;
@@ -397,6 +401,9 @@
*/
private static final int EVENT_REVALIDATE_NETWORK = 36;
+ // Handle changes in Private DNS settings.
+ private static final int EVENT_PRIVATE_DNS_SETTINGS_CHANGED = 37;
+
private static String eventName(int what) {
return sMagicDecoderRing.get(what, Integer.toString(what));
}
@@ -408,6 +415,7 @@
final private InternalHandler mHandler;
/** Handler used for incoming {@link NetworkStateTracker} events. */
final private NetworkStateTrackerHandler mTrackerHandler;
+ private final DnsManager mDnsManager;
private boolean mSystemReady;
private Intent mInitialBroadcast;
@@ -880,6 +888,9 @@
mMultinetworkPolicyTracker = createMultinetworkPolicyTracker(
mContext, mHandler, () -> rematchForAvoidBadWifiUpdate());
mMultinetworkPolicyTracker.start();
+
+ mDnsManager = new DnsManager(mContext, mNetd, mSystemProperties);
+ registerPrivateDnsSettingsCallbacks();
}
private Tethering makeTethering() {
@@ -940,6 +951,12 @@
EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON);
}
+ private void registerPrivateDnsSettingsCallbacks() {
+ for (Uri u : DnsManager.getPrivateDnsSettingsUris()) {
+ mSettingsObserver.observe(u, EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
+ }
+ }
+
private synchronized int nextNetworkRequestId() {
return mNextNetworkRequestId++;
}
@@ -995,9 +1012,9 @@
}
private Network[] getVpnUnderlyingNetworks(int uid) {
- if (!mLockdownEnabled) {
- int user = UserHandle.getUserId(uid);
- synchronized (mVpns) {
+ synchronized (mVpns) {
+ if (!mLockdownEnabled) {
+ int user = UserHandle.getUserId(uid);
Vpn vpn = mVpns.get(user);
if (vpn != null && vpn.appliesToUid(uid)) {
return vpn.getUnderlyingNetworks();
@@ -1085,8 +1102,10 @@
if (isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid, ignoreBlocked)) {
state.networkInfo.setDetailedState(DetailedState.BLOCKED, null, null);
}
- if (mLockdownTracker != null) {
- mLockdownTracker.augmentNetworkInfo(state.networkInfo);
+ synchronized (mVpns) {
+ if (mLockdownTracker != null) {
+ mLockdownTracker.augmentNetworkInfo(state.networkInfo);
+ }
}
}
@@ -1251,8 +1270,8 @@
result.put(nai.network, nc);
}
- if (!mLockdownEnabled) {
- synchronized (mVpns) {
+ synchronized (mVpns) {
+ if (!mLockdownEnabled) {
Vpn vpn = mVpns.get(userId);
if (vpn != null) {
Network[] networks = vpn.getUnderlyingNetworks();
@@ -1578,9 +1597,11 @@
}
private Intent makeGeneralIntent(NetworkInfo info, String bcastType) {
- if (mLockdownTracker != null) {
- info = new NetworkInfo(info);
- mLockdownTracker.augmentNetworkInfo(info);
+ synchronized (mVpns) {
+ if (mLockdownTracker != null) {
+ info = new NetworkInfo(info);
+ mLockdownTracker.augmentNetworkInfo(info);
+ }
}
Intent intent = new Intent(bcastType);
@@ -1826,24 +1847,6 @@
}
}
- private void flushVmDnsCache() {
- /*
- * Tell the VMs to toss their DNS caches
- */
- Intent intent = new Intent(Intent.ACTION_CLEAR_DNS_CACHE);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- /*
- * Connectivity events can happen before boot has completed ...
- */
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
@Override
public int getRestoreDefaultNetworkDelay(int networkType) {
String restoreDefaultNetworkDelayStr = mSystemProperties.get(
@@ -2122,36 +2125,59 @@
synchronized (mNetworkForNetId) {
nai = mNetworkForNetId.get(msg.arg2);
}
- if (nai != null) {
- final boolean valid =
- (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
- final boolean wasValidated = nai.lastValidated;
- final boolean wasDefault = isDefaultNetwork(nai);
- if (DBG) log(nai.name() + " validation " + (valid ? "passed" : "failed") +
- (msg.obj == null ? "" : " with redirect to " + (String)msg.obj));
- if (valid != nai.lastValidated) {
- if (wasDefault) {
- metricsLogger().defaultNetworkMetrics().logDefaultNetworkValidity(
- SystemClock.elapsedRealtime(), valid);
- }
- final int oldScore = nai.getCurrentScore();
- nai.lastValidated = valid;
- nai.everValidated |= valid;
- updateCapabilities(oldScore, nai, nai.networkCapabilities);
- // If score has changed, rebroadcast to NetworkFactories. b/17726566
- if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
+ if (nai == null) break;
+
+ final boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
+ final boolean wasValidated = nai.lastValidated;
+ final boolean wasDefault = isDefaultNetwork(nai);
+
+ final PrivateDnsConfig privateDnsCfg = (msg.obj instanceof PrivateDnsConfig)
+ ? (PrivateDnsConfig) msg.obj : null;
+ final String redirectUrl = (msg.obj instanceof String) ? (String) msg.obj : "";
+
+ final boolean reevaluationRequired;
+ final String logMsg;
+ if (valid) {
+ reevaluationRequired = updatePrivateDns(nai, privateDnsCfg);
+ logMsg = (DBG && (privateDnsCfg != null))
+ ? " with " + privateDnsCfg.toString() : "";
+ } else {
+ reevaluationRequired = false;
+ logMsg = (DBG && !TextUtils.isEmpty(redirectUrl))
+ ? " with redirect to " + redirectUrl : "";
+ }
+ if (DBG) {
+ log(nai.name() + " validation " + (valid ? "passed" : "failed") + logMsg);
+ }
+ // If there is a change in Private DNS configuration,
+ // trigger reevaluation of the network to test it.
+ if (reevaluationRequired) {
+ nai.networkMonitor.sendMessage(
+ NetworkMonitor.CMD_FORCE_REEVALUATION, Process.SYSTEM_UID);
+ break;
+ }
+ if (valid != nai.lastValidated) {
+ if (wasDefault) {
+ metricsLogger().defaultNetworkMetrics().logDefaultNetworkValidity(
+ SystemClock.elapsedRealtime(), valid);
}
- updateInetCondition(nai);
- // Let the NetworkAgent know the state of its network
- Bundle redirectUrlBundle = new Bundle();
- redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, (String)msg.obj);
- nai.asyncChannel.sendMessage(
- NetworkAgent.CMD_REPORT_NETWORK_STATUS,
- (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
- 0, redirectUrlBundle);
- if (wasValidated && !nai.lastValidated) {
- handleNetworkUnvalidated(nai);
- }
+ final int oldScore = nai.getCurrentScore();
+ nai.lastValidated = valid;
+ nai.everValidated |= valid;
+ updateCapabilities(oldScore, nai, nai.networkCapabilities);
+ // If score has changed, rebroadcast to NetworkFactories. b/17726566
+ if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
+ }
+ updateInetCondition(nai);
+ // Let the NetworkAgent know the state of its network
+ Bundle redirectUrlBundle = new Bundle();
+ redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, redirectUrl);
+ nai.asyncChannel.sendMessage(
+ NetworkAgent.CMD_REPORT_NETWORK_STATUS,
+ (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
+ 0, redirectUrlBundle);
+ if (wasValidated && !nai.lastValidated) {
+ handleNetworkUnvalidated(nai);
}
break;
}
@@ -2191,6 +2217,21 @@
}
break;
}
+ case NetworkMonitor.EVENT_PRIVATE_DNS_CONFIG_RESOLVED: {
+ final NetworkAgentInfo nai;
+ synchronized (mNetworkForNetId) {
+ nai = mNetworkForNetId.get(msg.arg2);
+ }
+ if (nai == null) break;
+
+ final PrivateDnsConfig cfg = (PrivateDnsConfig) msg.obj;
+ final boolean reevaluationRequired = updatePrivateDns(nai, cfg);
+ if (nai.lastValidated && reevaluationRequired) {
+ nai.networkMonitor.sendMessage(
+ NetworkMonitor.CMD_FORCE_REEVALUATION, Process.SYSTEM_UID);
+ }
+ break;
+ }
}
return true;
}
@@ -2226,6 +2267,63 @@
}
}
+ private void handlePrivateDnsSettingsChanged() {
+ final PrivateDnsConfig cfg = mDnsManager.getPrivateDnsConfig();
+
+ for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+ // Private DNS only ever applies to networks that might provide
+ // Internet access and therefore also require validation.
+ if (!NetworkMonitor.isValidationRequired(
+ mDefaultRequest.networkCapabilities, nai.networkCapabilities)) {
+ continue;
+ }
+
+ // Notify the NetworkMonitor thread in case it needs to cancel or
+ // schedule DNS resolutions. If a DNS resolution is required the
+ // result will be sent back to us.
+ nai.networkMonitor.notifyPrivateDnsSettingsChanged(cfg);
+
+ if (!cfg.inStrictMode()) {
+ // No strict mode hostname DNS resolution needed, so just update
+ // DNS settings directly. In opportunistic and "off" modes this
+ // just reprograms netd with the network-supplied DNS servers
+ // (and of course the boolean of whether or not to attempt TLS).
+ //
+ // TODO: Consider code flow parity with strict mode, i.e. having
+ // NetworkMonitor relay the PrivateDnsConfig back to us and then
+ // performing this call at that time.
+ updatePrivateDns(nai, cfg);
+ }
+ }
+ }
+
+ private boolean updatePrivateDns(NetworkAgentInfo nai, PrivateDnsConfig newCfg) {
+ final boolean reevaluationRequired = true;
+ final boolean dontReevaluate = false;
+
+ final PrivateDnsConfig oldCfg = mDnsManager.updatePrivateDns(nai.network, newCfg);
+ updateDnses(nai.linkProperties, null, nai.network.netId);
+
+ if (newCfg == null) {
+ if (oldCfg == null) return dontReevaluate;
+ return oldCfg.useTls ? reevaluationRequired : dontReevaluate;
+ }
+
+ if (oldCfg == null) {
+ return newCfg.useTls ? reevaluationRequired : dontReevaluate;
+ }
+
+ if (oldCfg.useTls != newCfg.useTls) {
+ return reevaluationRequired;
+ }
+
+ if (newCfg.inStrictMode() && !Objects.equals(oldCfg.hostname, newCfg.hostname)) {
+ return reevaluationRequired;
+ }
+
+ return dontReevaluate;
+ }
+
private void updateLingerState(NetworkAgentInfo nai, long now) {
// 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
// 2. If the network was lingering and there are now requests, unlinger it.
@@ -2360,6 +2458,7 @@
} catch (Exception e) {
loge("Exception removing network: " + e);
}
+ mDnsManager.removeNetwork(nai.network);
}
synchronized (mNetworkForNetId) {
mNetIdInUse.delete(nai.network.netId);
@@ -2516,6 +2615,7 @@
private void handleRemoveNetworkRequest(final NetworkRequestInfo nri) {
nri.unlinkDeathRecipient();
mNetworkRequests.remove(nri.request);
+
synchronized (mUidToNetworkRequestCount) {
int requests = mUidToNetworkRequestCount.get(nri.mUid, 0);
if (requests < 1) {
@@ -2528,6 +2628,7 @@
mUidToNetworkRequestCount.put(nri.mUid, requests - 1);
}
}
+
mNetworkRequestInfoLogs.log("RELEASE " + nri);
if (nri.request.isRequest()) {
boolean wasKept = false;
@@ -2904,6 +3005,9 @@
handleReportNetworkConnectivity((Network) msg.obj, msg.arg1, toBool(msg.arg2));
break;
}
+ case EVENT_PRIVATE_DNS_SETTINGS_CHANGED:
+ handlePrivateDnsSettingsChanged();
+ break;
}
}
}
@@ -3450,9 +3554,9 @@
public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage,
int userId) {
enforceCrossUserPermission(userId);
- throwIfLockdownEnabled();
synchronized (mVpns) {
+ throwIfLockdownEnabled();
Vpn vpn = mVpns.get(userId);
if (vpn != null) {
return vpn.prepare(oldPackage, newPackage);
@@ -3496,9 +3600,9 @@
*/
@Override
public ParcelFileDescriptor establishVpn(VpnConfig config) {
- throwIfLockdownEnabled();
int user = UserHandle.getUserId(Binder.getCallingUid());
synchronized (mVpns) {
+ throwIfLockdownEnabled();
return mVpns.get(user).establish(config);
}
}
@@ -3509,13 +3613,13 @@
*/
@Override
public void startLegacyVpn(VpnProfile profile) {
- throwIfLockdownEnabled();
+ int user = UserHandle.getUserId(Binder.getCallingUid());
final LinkProperties egress = getActiveLinkProperties();
if (egress == null) {
throw new IllegalStateException("Missing active network connection");
}
- int user = UserHandle.getUserId(Binder.getCallingUid());
synchronized (mVpns) {
+ throwIfLockdownEnabled();
mVpns.get(user).startLegacyVpn(profile, mKeyStore, egress);
}
}
@@ -3541,11 +3645,11 @@
@Override
public VpnInfo[] getAllVpnInfo() {
enforceConnectivityInternalPermission();
- if (mLockdownEnabled) {
- return new VpnInfo[0];
- }
-
synchronized (mVpns) {
+ if (mLockdownEnabled) {
+ return new VpnInfo[0];
+ }
+
List<VpnInfo> infoList = new ArrayList<>();
for (int i = 0; i < mVpns.size(); i++) {
VpnInfo info = createVpnInfo(mVpns.valueAt(i));
@@ -3610,33 +3714,33 @@
return false;
}
- // Tear down existing lockdown if profile was removed
- mLockdownEnabled = LockdownVpnTracker.isEnabled();
- if (mLockdownEnabled) {
- byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN);
- if (profileTag == null) {
- Slog.e(TAG, "Lockdown VPN configured but cannot be read from keystore");
- return false;
- }
- String profileName = new String(profileTag);
- final VpnProfile profile = VpnProfile.decode(
- profileName, mKeyStore.get(Credentials.VPN + profileName));
- if (profile == null) {
- Slog.e(TAG, "Lockdown VPN configured invalid profile " + profileName);
- setLockdownTracker(null);
- return true;
- }
- int user = UserHandle.getUserId(Binder.getCallingUid());
- synchronized (mVpns) {
+ synchronized (mVpns) {
+ // Tear down existing lockdown if profile was removed
+ mLockdownEnabled = LockdownVpnTracker.isEnabled();
+ if (mLockdownEnabled) {
+ byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN);
+ if (profileTag == null) {
+ Slog.e(TAG, "Lockdown VPN configured but cannot be read from keystore");
+ return false;
+ }
+ String profileName = new String(profileTag);
+ final VpnProfile profile = VpnProfile.decode(
+ profileName, mKeyStore.get(Credentials.VPN + profileName));
+ if (profile == null) {
+ Slog.e(TAG, "Lockdown VPN configured invalid profile " + profileName);
+ setLockdownTracker(null);
+ return true;
+ }
+ int user = UserHandle.getUserId(Binder.getCallingUid());
Vpn vpn = mVpns.get(user);
if (vpn == null) {
Slog.w(TAG, "VPN for user " + user + " not ready yet. Skipping lockdown");
return false;
}
setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, vpn, profile));
+ } else {
+ setLockdownTracker(null);
}
- } else {
- setLockdownTracker(null);
}
return true;
@@ -3646,6 +3750,7 @@
* Internally set new {@link LockdownVpnTracker}, shutting down any existing
* {@link LockdownVpnTracker}. Can be {@code null} to disable lockdown.
*/
+ @GuardedBy("mVpns")
private void setLockdownTracker(LockdownVpnTracker tracker) {
// Shutdown any existing tracker
final LockdownVpnTracker existing = mLockdownTracker;
@@ -3660,6 +3765,7 @@
}
}
+ @GuardedBy("mVpns")
private void throwIfLockdownEnabled() {
if (mLockdownEnabled) {
throw new IllegalStateException("Unavailable in lockdown mode");
@@ -3707,12 +3813,12 @@
enforceConnectivityInternalPermission();
enforceCrossUserPermission(userId);
- // Can't set always-on VPN if legacy VPN is already in lockdown mode.
- if (LockdownVpnTracker.isEnabled()) {
- return false;
- }
-
synchronized (mVpns) {
+ // Can't set always-on VPN if legacy VPN is already in lockdown mode.
+ if (LockdownVpnTracker.isEnabled()) {
+ return false;
+ }
+
Vpn vpn = mVpns.get(userId);
if (vpn == null) {
Slog.w(TAG, "User " + userId + " has no Vpn configuration");
@@ -3888,9 +3994,9 @@
}
userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, userId);
mVpns.put(userId, userVpn);
- }
- if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
- updateLockdownVpn();
+ if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
+ updateLockdownVpn();
+ }
}
}
@@ -3927,11 +4033,13 @@
}
private void onUserUnlocked(int userId) {
- // User present may be sent because of an unlock, which might mean an unlocked keystore.
- if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
- updateLockdownVpn();
- } else {
- startAlwaysOnVpn(userId);
+ synchronized (mVpns) {
+ // User present may be sent because of an unlock, which might mean an unlocked keystore.
+ if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
+ updateLockdownVpn();
+ } else {
+ startAlwaysOnVpn(userId);
+ }
}
}
@@ -4586,41 +4694,18 @@
return; // no updating necessary
}
- Collection<InetAddress> dnses = newLp.getDnsServers();
- if (DBG) log("Setting DNS servers for network " + netId + " to " + dnses);
+ final NetworkAgentInfo defaultNai = getDefaultNetwork();
+ final boolean isDefaultNetwork = (defaultNai != null && defaultNai.network.netId == netId);
+
+ if (DBG) {
+ final Collection<InetAddress> dnses = newLp.getDnsServers();
+ log("Setting DNS servers for network " + netId + " to " + dnses);
+ }
try {
- mNetd.setDnsConfigurationForNetwork(
- netId, NetworkUtils.makeStrings(dnses), newLp.getDomains());
+ mDnsManager.setDnsConfigurationForNetwork(netId, newLp, isDefaultNetwork);
} catch (Exception e) {
loge("Exception in setDnsConfigurationForNetwork: " + e);
}
- final NetworkAgentInfo defaultNai = getDefaultNetwork();
- if (defaultNai != null && defaultNai.network.netId == netId) {
- setDefaultDnsSystemProperties(dnses);
- }
- flushVmDnsCache();
- }
-
- private void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) {
- int last = 0;
- for (InetAddress dns : dnses) {
- ++last;
- setNetDnsProperty(last, dns.getHostAddress());
- }
- for (int i = last + 1; i <= mNumDnsEntries; ++i) {
- setNetDnsProperty(i, "");
- }
- mNumDnsEntries = last;
- }
-
- private void setNetDnsProperty(int which, String value) {
- final String key = "net.dns" + which;
- // Log and forget errors setting unsupported properties.
- try {
- mSystemProperties.set(key, value);
- } catch (Exception e) {
- Log.e(TAG, "Error setting unsupported net.dns property: ", e);
- }
}
private String getNetworkPermission(NetworkCapabilities nc) {
@@ -4635,51 +4720,67 @@
}
/**
- * Update the NetworkCapabilities for {@code networkAgent} to {@code networkCapabilities}
- * augmented with any stateful capabilities implied from {@code networkAgent}
- * (e.g., validated status and captive portal status).
- *
- * @param oldScore score of the network before any of the changes that prompted us
- * to call this function.
- * @param nai the network having its capabilities updated.
- * @param networkCapabilities the new network capabilities.
+ * Augments the NetworkCapabilities passed in by a NetworkAgent with capabilities that are
+ * maintained here that the NetworkAgent is not aware of (e.g., validated, captive portal,
+ * and foreground status).
*/
- private void updateCapabilities(
- int oldScore, NetworkAgentInfo nai, NetworkCapabilities networkCapabilities) {
+ private NetworkCapabilities mixInCapabilities(NetworkAgentInfo nai, NetworkCapabilities nc) {
// Once a NetworkAgent is connected, complain if some immutable capabilities are removed.
- if (nai.everConnected && !nai.networkCapabilities.satisfiedByImmutableNetworkCapabilities(
- networkCapabilities)) {
- // TODO: consider not complaining when a network agent degrade its capabilities if this
+ if (nai.everConnected &&
+ !nai.networkCapabilities.satisfiedByImmutableNetworkCapabilities(nc)) {
+ // TODO: consider not complaining when a network agent degrades its capabilities if this
// does not cause any request (that is not a listen) currently matching that agent to
// stop being matched by the updated agent.
- String diff = nai.networkCapabilities.describeImmutableDifferences(networkCapabilities);
+ String diff = nai.networkCapabilities.describeImmutableDifferences(nc);
if (!TextUtils.isEmpty(diff)) {
Slog.wtf(TAG, "BUG: " + nai + " lost immutable capabilities:" + diff);
}
}
// Don't modify caller's NetworkCapabilities.
- networkCapabilities = new NetworkCapabilities(networkCapabilities);
+ NetworkCapabilities newNc = new NetworkCapabilities(nc);
if (nai.lastValidated) {
- networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED);
+ newNc.addCapability(NET_CAPABILITY_VALIDATED);
} else {
- networkCapabilities.removeCapability(NET_CAPABILITY_VALIDATED);
+ newNc.removeCapability(NET_CAPABILITY_VALIDATED);
}
if (nai.lastCaptivePortalDetected) {
- networkCapabilities.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
+ newNc.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
} else {
- networkCapabilities.removeCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
+ newNc.removeCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
}
if (nai.isBackgroundNetwork()) {
- networkCapabilities.removeCapability(NET_CAPABILITY_FOREGROUND);
+ newNc.removeCapability(NET_CAPABILITY_FOREGROUND);
} else {
- networkCapabilities.addCapability(NET_CAPABILITY_FOREGROUND);
+ newNc.addCapability(NET_CAPABILITY_FOREGROUND);
}
- if (Objects.equals(nai.networkCapabilities, networkCapabilities)) return;
+ return newNc;
+ }
+
+ /**
+ * Update the NetworkCapabilities for {@code nai} to {@code nc}. Specifically:
+ *
+ * 1. Calls mixInCapabilities to merge the passed-in NetworkCapabilities {@code nc} with the
+ * capabilities we manage and store in {@code nai}, such as validated status and captive
+ * portal status)
+ * 2. Takes action on the result: changes network permissions, sends CAP_CHANGED callbacks, and
+ * potentially triggers rematches.
+ * 3. Directly informs other network stack components (NetworkStatsService, VPNs, etc. of the
+ * change.)
+ *
+ * @param oldScore score of the network before any of the changes that prompted us
+ * to call this function.
+ * @param nai the network having its capabilities updated.
+ * @param nc the new network capabilities.
+ */
+ private void updateCapabilities(int oldScore, NetworkAgentInfo nai, NetworkCapabilities nc) {
+ NetworkCapabilities newNc = mixInCapabilities(nai, nc);
+
+ if (Objects.equals(nai.networkCapabilities, newNc)) return;
final String oldPermission = getNetworkPermission(nai.networkCapabilities);
- final String newPermission = getNetworkPermission(networkCapabilities);
+ final String newPermission = getNetworkPermission(newNc);
if (!Objects.equals(oldPermission, newPermission) && nai.created && !nai.isVPN()) {
try {
mNetd.setNetworkPermission(nai.network.netId, newPermission);
@@ -4691,11 +4792,10 @@
final NetworkCapabilities prevNc;
synchronized (nai) {
prevNc = nai.networkCapabilities;
- nai.networkCapabilities = networkCapabilities;
+ nai.networkCapabilities = newNc;
}
- if (nai.getCurrentScore() == oldScore &&
- networkCapabilities.equalRequestableCapabilities(prevNc)) {
+ if (nai.getCurrentScore() == oldScore && newNc.equalRequestableCapabilities(prevNc)) {
// If the requestable capabilities haven't changed, and the score hasn't changed, then
// the change we're processing can't affect any requests, it can only affect the listens
// on this network. We might have been called by rematchNetworkAndRequests when a
@@ -4711,15 +4811,15 @@
// Report changes that are interesting for network statistics tracking.
if (prevNc != null) {
final boolean meteredChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_METERED) !=
- networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED);
+ newNc.hasCapability(NET_CAPABILITY_NOT_METERED);
final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING) !=
- networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
if (meteredChanged || roamingChanged) {
notifyIfacesChangedForNetworkStats();
}
}
- if (!networkCapabilities.hasTransport(TRANSPORT_VPN)) {
+ if (!newNc.hasTransport(TRANSPORT_VPN)) {
// Tell VPNs about updated capabilities, since they may need to
// bubble those changes through.
synchronized (mVpns) {
@@ -4893,7 +4993,7 @@
notifyLockdownVpn(newNetwork);
handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
updateTcpBufferSizes(newNetwork);
- setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
+ mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
}
private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) {
@@ -5243,11 +5343,13 @@
}
private void notifyLockdownVpn(NetworkAgentInfo nai) {
- if (mLockdownTracker != null) {
- if (nai != null && nai.isVPN()) {
- mLockdownTracker.onVpnStateChanged(nai.networkInfo);
- } else {
- mLockdownTracker.onNetworkInfoChanged();
+ synchronized (mVpns) {
+ if (mLockdownTracker != null) {
+ if (nai != null && nai.isVPN()) {
+ mLockdownTracker.onVpnStateChanged(nai.networkInfo);
+ } else {
+ mLockdownTracker.onNetworkInfoChanged();
+ }
}
}
}
@@ -5477,28 +5579,28 @@
@Override
public boolean addVpnAddress(String address, int prefixLength) {
- throwIfLockdownEnabled();
int user = UserHandle.getUserId(Binder.getCallingUid());
synchronized (mVpns) {
+ throwIfLockdownEnabled();
return mVpns.get(user).addAddress(address, prefixLength);
}
}
@Override
public boolean removeVpnAddress(String address, int prefixLength) {
- throwIfLockdownEnabled();
int user = UserHandle.getUserId(Binder.getCallingUid());
synchronized (mVpns) {
+ throwIfLockdownEnabled();
return mVpns.get(user).removeAddress(address, prefixLength);
}
}
@Override
public boolean setUnderlyingNetworksForVpn(Network[] networks) {
- throwIfLockdownEnabled();
int user = UserHandle.getUserId(Binder.getCallingUid());
- boolean success;
+ final boolean success;
synchronized (mVpns) {
+ throwIfLockdownEnabled();
success = mVpns.get(user).setUnderlyingNetworks(networks);
}
if (success) {
@@ -5558,31 +5660,31 @@
setAlwaysOnVpnPackage(userId, null, false);
setVpnPackageAuthorization(alwaysOnPackage, userId, false);
}
- }
- // Turn Always-on VPN off
- if (mLockdownEnabled && userId == UserHandle.USER_SYSTEM) {
- final long ident = Binder.clearCallingIdentity();
- try {
- mKeyStore.delete(Credentials.LOCKDOWN_VPN);
- mLockdownEnabled = false;
- setLockdownTracker(null);
- } finally {
- Binder.restoreCallingIdentity(ident);
+ // Turn Always-on VPN off
+ if (mLockdownEnabled && userId == UserHandle.USER_SYSTEM) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mKeyStore.delete(Credentials.LOCKDOWN_VPN);
+ mLockdownEnabled = false;
+ setLockdownTracker(null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
- }
- // Turn VPN off
- VpnConfig vpnConfig = getVpnConfig(userId);
- if (vpnConfig != null) {
- if (vpnConfig.legacy) {
- prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, userId);
- } else {
- // Prevent this app (packagename = vpnConfig.user) from initiating VPN connections
- // in the future without user intervention.
- setVpnPackageAuthorization(vpnConfig.user, userId, false);
+ // Turn VPN off
+ VpnConfig vpnConfig = getVpnConfig(userId);
+ if (vpnConfig != null) {
+ if (vpnConfig.legacy) {
+ prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, userId);
+ } else {
+ // Prevent this app (packagename = vpnConfig.user) from initiating
+ // VPN connections in the future without user intervention.
+ setVpnPackageAuthorization(vpnConfig.user, userId, false);
- prepareVpn(null, VpnConfig.LEGACY_VPN, userId);
+ prepareVpn(null, VpnConfig.LEGACY_VPN, userId);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 985f16d..a12c85a 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -1591,6 +1591,8 @@
mPowerSaveWhitelistExceptIdleAppIdArray = buildAppIdArray(
mPowerSaveWhitelistAppsExceptIdle, mPowerSaveWhitelistUserApps,
mPowerSaveWhitelistExceptIdleAppIds);
+
+ passWhiteListToForceAppStandbyTrackerLocked();
}
return true;
} catch (PackageManager.NameNotFoundException e) {
@@ -1608,6 +1610,8 @@
mPowerSaveWhitelistAppsExceptIdle, mPowerSaveWhitelistUserApps,
mPowerSaveWhitelistExceptIdleAppIds);
mPowerSaveWhitelistUserAppsExceptIdle.clear();
+
+ passWhiteListToForceAppStandbyTrackerLocked();
}
}
}
@@ -2572,7 +2576,7 @@
private void passWhiteListToForceAppStandbyTrackerLocked() {
ForceAppStandbyTracker.getInstance(getContext()).setPowerSaveWhitelistAppIds(
- mPowerSaveWhitelistAllAppIdArray,
+ mPowerSaveWhitelistExceptIdleAppIdArray,
mTempWhitelistAppIdArray);
}
diff --git a/services/core/java/com/android/server/DiskStatsService.java b/services/core/java/com/android/server/DiskStatsService.java
index 2d2c6b0..e884de0 100644
--- a/services/core/java/com/android/server/DiskStatsService.java
+++ b/services/core/java/com/android/server/DiskStatsService.java
@@ -19,6 +19,10 @@
import android.content.Context;
import android.os.Binder;
import android.os.Environment;
+import android.os.IBinder;
+import android.os.IStoraged;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.StatFs;
import android.os.SystemClock;
import android.os.storage.StorageManager;
@@ -109,6 +113,12 @@
}
}
+ if (protoFormat) {
+ reportDiskWriteSpeedProto(proto);
+ } else {
+ reportDiskWriteSpeed(pw);
+ }
+
reportFreeSpace(Environment.getDataDirectory(), "Data", pw, proto,
DiskStatsFreeSpaceProto.FOLDER_DATA);
reportFreeSpace(Environment.getDownloadCacheDirectory(), "Cache", pw, proto,
@@ -285,4 +295,41 @@
Log.w(TAG, "exception reading diskstats cache file", e);
}
}
+
+ private int getRecentPerf() throws RemoteException, IllegalStateException {
+ IBinder binder = ServiceManager.getService("storaged");
+ if (binder == null) throw new IllegalStateException("storaged not found");
+ IStoraged storaged = IStoraged.Stub.asInterface(binder);
+ return storaged.getRecentPerf();
+ }
+
+ // Keep reportDiskWriteSpeed and reportDiskWriteSpeedProto in sync
+ private void reportDiskWriteSpeed(PrintWriter pw) {
+ try {
+ long perf = getRecentPerf();
+ if (perf != 0) {
+ pw.print("Recent Disk Write Speed (kB/s) = ");
+ pw.println(perf);
+ } else {
+ pw.println("Recent Disk Write Speed data unavailable");
+ Log.w(TAG, "Recent Disk Write Speed data unavailable!");
+ }
+ } catch (RemoteException | IllegalStateException e) {
+ pw.println(e.toString());
+ Log.e(TAG, e.toString());
+ }
+ }
+
+ private void reportDiskWriteSpeedProto(ProtoOutputStream proto) {
+ try {
+ long perf = getRecentPerf();
+ if (perf != 0) {
+ proto.write(DiskStatsServiceDumpProto.BENCHMARKED_WRITE_SPEED_KBPS, perf);
+ } else {
+ Log.w(TAG, "Recent Disk Write Speed data unavailable!");
+ }
+ } catch (RemoteException | IllegalStateException e) {
+ Log.e(TAG, e.toString());
+ }
+ }
}
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 8361132..732ac66 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -133,6 +133,7 @@
2846 full_backup_cancelled (Package|3),(Message|3)
2850 backup_transport_lifecycle (Transport|3),(Bound|1|1)
+2851 backup_transport_connection (Transport|3),(Connected|1|1)
# ---------------------------
diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java
index 61d3833..4551611 100644
--- a/services/core/java/com/android/server/ForceAppStandbyTracker.java
+++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java
@@ -25,6 +25,9 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.BatteryManager;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -33,8 +36,10 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.ArraySet;
import android.util.Pair;
+import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.proto.ProtoOutputStream;
@@ -59,15 +64,16 @@
* - Global "force all apps standby" mode enforced by battery saver.
*
* TODO: In general, we can reduce the number of callbacks by checking all signals before sending
- * each callback. For example, even when an UID comes into the foreground, if it wasn't
- * originally restricted, then there's no need to send an event.
- * Doing this would be error-prone, so we punt it for now, but we should revisit it later.
+ * each callback. For example, even when an UID comes into the foreground, if it wasn't
+ * originally restricted, then there's no need to send an event.
+ * Doing this would be error-prone, so we punt it for now, but we should revisit it later.
*
* Test:
- atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+ * atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
*/
public class ForceAppStandbyTracker {
private static final String TAG = "ForceAppStandbyTracker";
+ private static final boolean DEBUG = false;
@GuardedBy("ForceAppStandbyTracker.class")
private static ForceAppStandbyTracker sInstance;
@@ -85,6 +91,9 @@
private final MyHandler mHandler;
+ @VisibleForTesting
+ FeatureFlagsObserver mFlagsObserver;
+
/**
* Pair of (uid (not user-id), packageName) with OP_RUN_ANY_IN_BACKGROUND *not* allowed.
*/
@@ -94,6 +103,9 @@
@GuardedBy("mLock")
final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
+ /**
+ * System except-idle + user whitelist in the device idle controller.
+ */
@GuardedBy("mLock")
private int[] mPowerWhitelistedAllAppIds = new int[0];
@@ -107,8 +119,89 @@
boolean mStarted;
@GuardedBy("mLock")
+ boolean mIsCharging;
+
+ @GuardedBy("mLock")
+ boolean mBatterySaverEnabled;
+
+ /**
+ * True if the forced app standby is currently enabled
+ */
+ @GuardedBy("mLock")
boolean mForceAllAppsStandby;
+ /**
+ * True if the forced app standby for small battery devices feature is enabled in settings
+ */
+ @GuardedBy("mLock")
+ boolean mForceAllAppStandbyForSmallBattery;
+
+ /**
+ * True if the forced app standby feature is enabled in settings
+ */
+ @GuardedBy("mLock")
+ boolean mForcedAppStandbyEnabled;
+
+ @VisibleForTesting
+ class FeatureFlagsObserver extends ContentObserver {
+ FeatureFlagsObserver() {
+ super(null);
+ }
+
+ void register() {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED),
+ false, this);
+
+ mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED), false, this);
+ }
+
+ boolean isForcedAppStandbyEnabled() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.FORCED_APP_STANDBY_ENABLED, 1) == 1;
+ }
+
+ boolean isForcedAppStandbyForSmallBatteryEnabled() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 0) == 1;
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED).equals(uri)) {
+ final boolean enabled = isForcedAppStandbyEnabled();
+ synchronized (mLock) {
+ if (mForcedAppStandbyEnabled == enabled) {
+ return;
+ }
+ mForcedAppStandbyEnabled = enabled;
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Forced app standby feature flag changed: " + mForcedAppStandbyEnabled);
+ }
+ }
+ mHandler.notifyForcedAppStandbyFeatureFlagChanged();
+ } else if (Settings.Global.getUriFor(
+ Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED).equals(uri)) {
+ final boolean enabled = isForcedAppStandbyForSmallBatteryEnabled();
+ synchronized (mLock) {
+ if (mForceAllAppStandbyForSmallBattery == enabled) {
+ return;
+ }
+ mForceAllAppStandbyForSmallBattery = enabled;
+ if (DEBUG) {
+ Slog.d(TAG, "Forced app standby for small battery feature flag changed: "
+ + mForceAllAppStandbyForSmallBattery);
+ }
+ updateForceAllAppStandbyState();
+ }
+ } else {
+ Slog.w(TAG, "Unexpected feature flag uri encountered: " + uri);
+ }
+ }
+ }
+
public static abstract class Listener {
/**
* This is called when the OP_RUN_ANY_IN_BACKGROUND appops changed for a package.
@@ -246,6 +339,11 @@
mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager());
mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService());
mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal());
+ mFlagsObserver = new FeatureFlagsObserver();
+ mFlagsObserver.register();
+ mForcedAppStandbyEnabled = mFlagsObserver.isForcedAppStandbyEnabled();
+ mForceAllAppStandbyForSmallBattery =
+ mFlagsObserver.isForcedAppStandbyForSmallBatteryEnabled();
try {
mIActivityManager.registerUidObserver(new UidObserver(),
@@ -260,16 +358,24 @@
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_REMOVED);
+ filter.addAction(Intent.ACTION_BATTERY_CHANGED);
mContext.registerReceiver(new MyReceiver(), filter);
refreshForcedAppStandbyUidPackagesLocked();
mPowerManagerInternal.registerLowPowerModeObserver(
ServiceType.FORCE_ALL_APPS_STANDBY,
- (state) -> updateForceAllAppsStandby(state.batterySaverEnabled));
+ (state) -> {
+ synchronized (mLock) {
+ mBatterySaverEnabled = state.batterySaverEnabled;
+ updateForceAllAppStandbyState();
+ }
+ });
- updateForceAllAppsStandby(mPowerManagerInternal.getLowPowerState(
- ServiceType.FORCE_ALL_APPS_STANDBY).batterySaverEnabled);
+ mBatterySaverEnabled = mPowerManagerInternal.getLowPowerState(
+ ServiceType.FORCE_ALL_APPS_STANDBY).batterySaverEnabled;
+
+ updateForceAllAppStandbyState();
}
}
@@ -294,6 +400,11 @@
return LocalServices.getService(PowerManagerInternal.class);
}
+ @VisibleForTesting
+ boolean isSmallBatteryDevice() {
+ return ActivityManager.isSmallBatteryDevice();
+ }
+
/**
* Update {@link #mRunAnyRestrictedPackages} with the current app ops state.
*/
@@ -323,18 +434,29 @@
}
}
+ private void updateForceAllAppStandbyState() {
+ synchronized (mLock) {
+ if (mIsCharging) {
+ toggleForceAllAppsStandbyLocked(false);
+ } else if (mForceAllAppStandbyForSmallBattery
+ && isSmallBatteryDevice()) {
+ toggleForceAllAppsStandbyLocked(true);
+ } else {
+ toggleForceAllAppsStandbyLocked(mBatterySaverEnabled);
+ }
+ }
+ }
+
/**
* Update {@link #mForceAllAppsStandby} and notifies the listeners.
*/
- void updateForceAllAppsStandby(boolean enable) {
- synchronized (mLock) {
- if (enable == mForceAllAppsStandby) {
- return;
- }
- mForceAllAppsStandby = enable;
-
- mHandler.notifyForceAllAppsStandbyChanged();
+ private void toggleForceAllAppsStandbyLocked(boolean enable) {
+ if (enable == mForceAllAppsStandby) {
+ return;
}
+ mForceAllAppsStandby = enable;
+
+ mHandler.notifyForceAllAppsStandbyChanged();
}
private int findForcedAppStandbyUidPackageIndexLocked(int uid, @NonNull String packageName) {
@@ -364,7 +486,7 @@
*/
boolean updateForcedAppStandbyUidPackageLocked(int uid, @NonNull String packageName,
boolean restricted) {
- final int index = findForcedAppStandbyUidPackageIndexLocked(uid, packageName);
+ final int index = findForcedAppStandbyUidPackageIndexLocked(uid, packageName);
final boolean wasRestricted = index >= 0;
if (wasRestricted == restricted) {
return false;
@@ -418,25 +540,30 @@
}
private final class UidObserver extends IUidObserver.Stub {
- @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
+ @Override
+ public void onUidStateChanged(int uid, int procState, long procStateSeq) {
}
- @Override public void onUidGone(int uid, boolean disabled) {
+ @Override
+ public void onUidGone(int uid, boolean disabled) {
uidToBackground(uid, /*remove=*/ true);
}
- @Override public void onUidActive(int uid) {
+ @Override
+ public void onUidActive(int uid) {
uidToForeground(uid);
}
- @Override public void onUidIdle(int uid, boolean disabled) {
+ @Override
+ public void onUidIdle(int uid, boolean disabled) {
// Just to avoid excessive memcpy, don't remove from the array in this case.
uidToBackground(uid, /*remove=*/ false);
}
- @Override public void onUidCachedChanged(int uid, boolean cached) {
+ @Override
+ public void onUidCachedChanged(int uid, boolean cached) {
}
- };
+ }
private final class AppOpsWatcher extends IAppOpsCallback.Stub {
@Override
@@ -464,6 +591,13 @@
if (userId > 0) {
mHandler.doUserRemoved(userId);
}
+ } else if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
+ int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
+ synchronized (mLock) {
+ mIsCharging = (status == BatteryManager.BATTERY_STATUS_CHARGING
+ || status == BatteryManager.BATTERY_STATUS_FULL);
+ }
+ updateForceAllAppStandbyState();
}
}
}
@@ -481,8 +615,8 @@
private static final int MSG_ALL_WHITELIST_CHANGED = 4;
private static final int MSG_TEMP_WHITELIST_CHANGED = 5;
private static final int MSG_FORCE_ALL_CHANGED = 6;
-
private static final int MSG_USER_REMOVED = 7;
+ private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8;
public MyHandler(Looper looper) {
super(looper);
@@ -491,6 +625,7 @@
public void notifyUidForegroundStateChanged(int uid) {
obtainMessage(MSG_UID_STATE_CHANGED, uid, 0).sendToTarget();
}
+
public void notifyRunAnyAppOpsChanged(int uid, @NonNull String packageName) {
obtainMessage(MSG_RUN_ANY_CHANGED, uid, 0, packageName).sendToTarget();
}
@@ -511,12 +646,16 @@
obtainMessage(MSG_FORCE_ALL_CHANGED).sendToTarget();
}
+ public void notifyForcedAppStandbyFeatureFlagChanged() {
+ obtainMessage(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED).sendToTarget();
+ }
+
public void doUserRemoved(int userId) {
obtainMessage(MSG_USER_REMOVED, userId, 0).sendToTarget();
}
@Override
- public void dispatchMessage(Message msg) {
+ public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_USER_REMOVED:
handleUserRemoved(msg.arg1);
@@ -562,6 +701,19 @@
l.onForceAllAppsStandbyChanged(sender);
}
return;
+ case MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED:
+ // Feature flag for forced app standby changed.
+ final boolean unblockAlarms;
+ synchronized (mLock) {
+ unblockAlarms = !mForcedAppStandbyEnabled && !mForceAllAppsStandby;
+ }
+ for (Listener l: cloneListeners()) {
+ l.updateAllJobs();
+ if (unblockAlarms) {
+ l.unblockAllUnrestrictedAlarms();
+ }
+ }
+ return;
case MSG_USER_REMOVED:
handleUserRemoved(msg.arg1);
return;
@@ -701,7 +853,7 @@
return true;
}
- return isRunAnyRestrictedLocked(uid, packageName);
+ return mForcedAppStandbyEnabled && isRunAnyRestrictedLocked(uid, packageName);
}
}
@@ -766,10 +918,25 @@
public void dump(PrintWriter pw, String indent) {
synchronized (mLock) {
pw.print(indent);
+ pw.println("Forced App Standby Feature enabled: " + mForcedAppStandbyEnabled);
+
+ pw.print(indent);
pw.print("Force all apps standby: ");
pw.println(isForceAllAppsStandbyEnabled());
pw.print(indent);
+ pw.print("Small Battery Device: ");
+ pw.println(isSmallBatteryDevice());
+
+ pw.print(indent);
+ pw.print("Force all apps standby for small battery device: ");
+ pw.println(mForceAllAppStandbyForSmallBattery);
+
+ pw.print(indent);
+ pw.print("Charging: ");
+ pw.println(mIsCharging);
+
+ pw.print(indent);
pw.print("Foreground uids: [");
String sep = "";
@@ -808,6 +975,11 @@
final long token = proto.start(fieldId);
proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY, mForceAllAppsStandby);
+ proto.write(ForceAppStandbyTrackerProto.IS_SMALL_BATTERY_DEVICE,
+ isSmallBatteryDevice());
+ proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY_FOR_SMALL_BATTERY,
+ mForceAllAppStandbyForSmallBattery);
+ proto.write(ForceAppStandbyTrackerProto.IS_CHARGING, mIsCharging);
for (int i = 0; i < mForegroundUids.size(); i++) {
if (mForegroundUids.valueAt(i)) {
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 02cfe3d..46a35ec 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -25,6 +25,7 @@
import static com.android.internal.util.Preconditions.checkNotNull;
import android.content.Context;
+import android.net.ConnectivityManager;
import android.net.IIpSecService;
import android.net.INetd;
import android.net.IpSecAlgorithm;
@@ -62,7 +63,6 @@
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
import libcore.io.IoUtils;
@@ -83,7 +83,7 @@
private static final String NETD_SERVICE_NAME = "netd";
private static final int[] DIRECTIONS =
- new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN};
+ new int[] {IpSecManager.DIRECTION_OUT, IpSecManager.DIRECTION_IN};
private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
private static final int MAX_PORT_BIND_ATTEMPTS = 10;
@@ -104,10 +104,10 @@
private final Context mContext;
/**
- * The next non-repeating global ID for tracking resources between users, this service,
- * and kernel data structures. Accessing this variable is not thread safe, so it is
- * only read or modified within blocks synchronized on IpSecService.this. We want to
- * avoid -1 (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it).
+ * The next non-repeating global ID for tracking resources between users, this service, and
+ * kernel data structures. Accessing this variable is not thread safe, so it is only read or
+ * modified within blocks synchronized on IpSecService.this. We want to avoid -1
+ * (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it).
*/
@GuardedBy("IpSecService.this")
private int mNextResourceId = 1;
@@ -536,14 +536,14 @@
private final class TransformRecord extends KernelResourceRecord {
private final IpSecConfig mConfig;
- private final SpiRecord[] mSpis;
+ private final SpiRecord mSpi;
private final EncapSocketRecord mSocket;
TransformRecord(
- int resourceId, IpSecConfig config, SpiRecord[] spis, EncapSocketRecord socket) {
+ int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) {
super(resourceId);
mConfig = config;
- mSpis = spis;
+ mSpi = spi;
mSocket = socket;
}
@@ -551,29 +551,26 @@
return mConfig;
}
- public SpiRecord getSpiRecord(int direction) {
- return mSpis[direction];
+ public SpiRecord getSpiRecord() {
+ return mSpi;
}
/** always guarded by IpSecService#this */
@Override
public void freeUnderlyingResources() {
- for (int direction : DIRECTIONS) {
- int spi = mSpis[direction].getSpi();
- try {
- mSrvConfig
- .getNetdInstance()
- .ipSecDeleteSecurityAssociation(
- mResourceId,
- direction,
- mConfig.getLocalAddress(),
- mConfig.getRemoteAddress(),
- spi);
- } catch (ServiceSpecificException e) {
- // FIXME: get the error code and throw is at an IOException from Errno Exception
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to delete SA with ID: " + mResourceId);
- }
+ int spi = mSpi.getSpi();
+ try {
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecDeleteSecurityAssociation(
+ mResourceId,
+ mConfig.getSourceAddress(),
+ mConfig.getDestinationAddress(),
+ spi);
+ } catch (ServiceSpecificException e) {
+ // FIXME: get the error code and throw is at an IOException from Errno Exception
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to delete SA with ID: " + mResourceId);
}
getResourceTracker().give();
@@ -597,10 +594,8 @@
.append(super.toString())
.append(", mSocket=")
.append(mSocket)
- .append(", mSpis[OUT].mResourceId=")
- .append(mSpis[IpSecTransform.DIRECTION_OUT].mResourceId)
- .append(", mSpis[IN].mResourceId=")
- .append(mSpis[IpSecTransform.DIRECTION_IN].mResourceId)
+ .append(", mSpi.mResourceId=")
+ .append(mSpi.mResourceId)
.append(", mConfig=")
.append(mConfig)
.append("}");
@@ -609,23 +604,16 @@
}
private final class SpiRecord extends KernelResourceRecord {
- private final int mDirection;
- private final String mLocalAddress;
- private final String mRemoteAddress;
+ private final String mSourceAddress;
+ private final String mDestinationAddress;
private int mSpi;
private boolean mOwnedByTransform = false;
- SpiRecord(
- int resourceId,
- int direction,
- String localAddress,
- String remoteAddress,
- int spi) {
+ SpiRecord(int resourceId, String sourceAddress, String destinationAddress, int spi) {
super(resourceId);
- mDirection = direction;
- mLocalAddress = localAddress;
- mRemoteAddress = remoteAddress;
+ mSourceAddress = sourceAddress;
+ mDestinationAddress = destinationAddress;
mSpi = spi;
}
@@ -646,7 +634,7 @@
mSrvConfig
.getNetdInstance()
.ipSecDeleteSecurityAssociation(
- mResourceId, mDirection, mLocalAddress, mRemoteAddress, mSpi);
+ mResourceId, mSourceAddress, mDestinationAddress, mSpi);
} catch (ServiceSpecificException e) {
// FIXME: get the error code and throw is at an IOException from Errno Exception
} catch (RemoteException e) {
@@ -662,6 +650,10 @@
return mSpi;
}
+ public String getDestinationAddress() {
+ return mDestinationAddress;
+ }
+
public void setOwnedByTransform() {
if (mOwnedByTransform) {
// Programming error
@@ -689,12 +681,10 @@
.append(super.toString())
.append(", mSpi=")
.append(mSpi)
- .append(", mDirection=")
- .append(mDirection)
- .append(", mLocalAddress=")
- .append(mLocalAddress)
- .append(", mRemoteAddress=")
- .append(mRemoteAddress)
+ .append(", mSourceAddress=")
+ .append(mSourceAddress)
+ .append(", mDestinationAddress=")
+ .append(mDestinationAddress)
.append(", mOwnedByTransform=")
.append(mOwnedByTransform)
.append("}");
@@ -772,14 +762,17 @@
/** @hide */
@VisibleForTesting
public IpSecService(Context context, IpSecServiceConfiguration config) {
- this(context, config, (fd, uid) -> {
- try{
- TrafficStats.setThreadStatsUid(uid);
- TrafficStats.tagFileDescriptor(fd);
- } finally {
- TrafficStats.clearThreadStatsUid();
- }
- });
+ this(
+ context,
+ config,
+ (fd, uid) -> {
+ try {
+ TrafficStats.setThreadStatsUid(uid);
+ TrafficStats.tagFileDescriptor(fd);
+ } finally {
+ TrafficStats.clearThreadStatsUid();
+ }
+ });
}
/** @hide */
@@ -845,8 +838,8 @@
*/
private static void checkDirection(int direction) {
switch (direction) {
- case IpSecTransform.DIRECTION_OUT:
- case IpSecTransform.DIRECTION_IN:
+ case IpSecManager.DIRECTION_OUT:
+ case IpSecManager.DIRECTION_IN:
return;
}
throw new IllegalArgumentException("Invalid Direction: " + direction);
@@ -855,10 +848,8 @@
/** Get a new SPI and maintain the reservation in the system server */
@Override
public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
- int direction, String remoteAddress, int requestedSpi, IBinder binder)
- throws RemoteException {
- checkDirection(direction);
- checkInetAddress(remoteAddress);
+ String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
+ checkInetAddress(destinationAddress);
/* requestedSpi can be anything in the int range, so no check is needed. */
checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
@@ -866,28 +857,21 @@
final int resourceId = mNextResourceId++;
int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
- String localAddress = "";
-
try {
if (!userRecord.mSpiQuotaTracker.isAvailable()) {
return new IpSecSpiResponse(
IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
}
+
spi =
mSrvConfig
.getNetdInstance()
- .ipSecAllocateSpi(
- resourceId,
- direction,
- localAddress,
- remoteAddress,
- requestedSpi);
+ .ipSecAllocateSpi(resourceId, "", destinationAddress, requestedSpi);
Log.d(TAG, "Allocated SPI " + spi);
userRecord.mSpiRecords.put(
resourceId,
new RefcountedResource<SpiRecord>(
- new SpiRecord(resourceId, direction, localAddress, remoteAddress, spi),
- binder));
+ new SpiRecord(resourceId, "", destinationAddress, spi), binder));
} catch (ServiceSpecificException e) {
// TODO: Add appropriate checks when other ServiceSpecificException types are supported
return new IpSecSpiResponse(
@@ -1032,27 +1016,27 @@
}
@VisibleForTesting
- void validateAlgorithms(IpSecConfig config, int direction) throws IllegalArgumentException {
- IpSecAlgorithm auth = config.getAuthentication(direction);
- IpSecAlgorithm crypt = config.getEncryption(direction);
- IpSecAlgorithm aead = config.getAuthenticatedEncryption(direction);
+ void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
+ IpSecAlgorithm auth = config.getAuthentication();
+ IpSecAlgorithm crypt = config.getEncryption();
+ IpSecAlgorithm aead = config.getAuthenticatedEncryption();
- // Validate the algorithm set
- Preconditions.checkArgument(
- aead != null || crypt != null || auth != null,
- "No Encryption or Authentication algorithms specified");
- Preconditions.checkArgument(
- auth == null || auth.isAuthentication(),
- "Unsupported algorithm for Authentication");
- Preconditions.checkArgument(
+ // Validate the algorithm set
+ Preconditions.checkArgument(
+ aead != null || crypt != null || auth != null,
+ "No Encryption or Authentication algorithms specified");
+ Preconditions.checkArgument(
+ auth == null || auth.isAuthentication(),
+ "Unsupported algorithm for Authentication");
+ Preconditions.checkArgument(
crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
- Preconditions.checkArgument(
- aead == null || aead.isAead(),
- "Unsupported algorithm for Authenticated Encryption");
- Preconditions.checkArgument(
- aead == null || (auth == null && crypt == null),
- "Authenticated Encryption is mutually exclusive with other Authentication "
- + "or Encryption algorithms");
+ Preconditions.checkArgument(
+ aead == null || aead.isAead(),
+ "Unsupported algorithm for Authenticated Encryption");
+ Preconditions.checkArgument(
+ aead == null || (auth == null && crypt == null),
+ "Authenticated Encryption is mutually exclusive with other Authentication "
+ + "or Encryption algorithms");
}
/**
@@ -1062,29 +1046,6 @@
private void checkIpSecConfig(IpSecConfig config) {
UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
- if (config.getLocalAddress() == null) {
- throw new IllegalArgumentException("Invalid null Local InetAddress");
- }
-
- if (config.getRemoteAddress() == null) {
- throw new IllegalArgumentException("Invalid null Remote InetAddress");
- }
-
- switch (config.getMode()) {
- case IpSecTransform.MODE_TRANSPORT:
- if (!config.getLocalAddress().isEmpty()) {
- throw new IllegalArgumentException("Non-empty Local Address");
- }
- // Must be valid, and not a wildcard
- checkInetAddress(config.getRemoteAddress());
- break;
- case IpSecTransform.MODE_TUNNEL:
- break;
- default:
- throw new IllegalArgumentException(
- "Invalid IpSecTransform.mode: " + config.getMode());
- }
-
switch (config.getEncapType()) {
case IpSecTransform.ENCAP_NONE:
break;
@@ -1103,11 +1064,36 @@
throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
}
- for (int direction : DIRECTIONS) {
- validateAlgorithms(config, direction);
+ validateAlgorithms(config);
- // Retrieve SPI record; will throw IllegalArgumentException if not found
- userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId(direction));
+ // Retrieve SPI record; will throw IllegalArgumentException if not found
+ SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
+
+ // If no remote address is supplied, then use one from the SPI.
+ if (TextUtils.isEmpty(config.getDestinationAddress())) {
+ config.setDestinationAddress(s.getDestinationAddress());
+ }
+
+ // All remote addresses must match
+ if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
+ throw new IllegalArgumentException("Mismatched remote addresseses.");
+ }
+
+ // This check is technically redundant due to the chain of custody between the SPI and
+ // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
+ // the transform, this will prevent us from messing up.
+ checkInetAddress(config.getDestinationAddress());
+
+ // Require a valid source address for all transforms.
+ checkInetAddress(config.getSourceAddress());
+
+ switch (config.getMode()) {
+ case IpSecTransform.MODE_TRANSPORT:
+ case IpSecTransform.MODE_TUNNEL:
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Invalid IpSecTransform.mode: " + config.getMode());
}
}
@@ -1127,13 +1113,12 @@
UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
- // Avoid resizing by creating a dependency array of min-size 3 (1 UDP encap + 2 SPIs)
- List<RefcountedResource> dependencies = new ArrayList<>(3);
+ // Avoid resizing by creating a dependency array of min-size 2 (1 UDP encap + 1 SPI)
+ List<RefcountedResource> dependencies = new ArrayList<>(2);
if (!userRecord.mTransformQuotaTracker.isAvailable()) {
return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
}
- SpiRecord[] spis = new SpiRecord[DIRECTIONS.length];
int encapType, encapLocalPort = 0, encapRemotePort = 0;
EncapSocketRecord socketRecord = null;
@@ -1149,51 +1134,46 @@
encapRemotePort = c.getEncapRemotePort();
}
- for (int direction : DIRECTIONS) {
- IpSecAlgorithm auth = c.getAuthentication(direction);
- IpSecAlgorithm crypt = c.getEncryption(direction);
- IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption(direction);
+ IpSecAlgorithm auth = c.getAuthentication();
+ IpSecAlgorithm crypt = c.getEncryption();
+ IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
- RefcountedResource<SpiRecord> refcountedSpiRecord =
- userRecord.mSpiRecords.getRefcountedResourceOrThrow(
- c.getSpiResourceId(direction));
- dependencies.add(refcountedSpiRecord);
+ RefcountedResource<SpiRecord> refcountedSpiRecord =
+ userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
+ dependencies.add(refcountedSpiRecord);
+ SpiRecord spiRecord = refcountedSpiRecord.getResource();
- spis[direction] = refcountedSpiRecord.getResource();
- int spi = spis[direction].getSpi();
- try {
- mSrvConfig
- .getNetdInstance()
- .ipSecAddSecurityAssociation(
- resourceId,
- c.getMode(),
- direction,
- c.getLocalAddress(),
- c.getRemoteAddress(),
- (c.getNetwork() != null) ? c.getNetwork().getNetworkHandle() : 0,
- spi,
- (auth != null) ? auth.getName() : "",
- (auth != null) ? auth.getKey() : new byte[] {},
- (auth != null) ? auth.getTruncationLengthBits() : 0,
- (crypt != null) ? crypt.getName() : "",
- (crypt != null) ? crypt.getKey() : new byte[] {},
- (crypt != null) ? crypt.getTruncationLengthBits() : 0,
- (authCrypt != null) ? authCrypt.getName() : "",
- (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
- (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
- encapType,
- encapLocalPort,
- encapRemotePort);
- } catch (ServiceSpecificException e) {
- // FIXME: get the error code and throw is at an IOException from Errno Exception
- return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
- }
+ try {
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecAddSecurityAssociation(
+ resourceId,
+ c.getMode(),
+ c.getSourceAddress(),
+ c.getDestinationAddress(),
+ (c.getNetwork() != null) ? c.getNetwork().netId : 0,
+ spiRecord.getSpi(),
+ (auth != null) ? auth.getName() : "",
+ (auth != null) ? auth.getKey() : new byte[] {},
+ (auth != null) ? auth.getTruncationLengthBits() : 0,
+ (crypt != null) ? crypt.getName() : "",
+ (crypt != null) ? crypt.getKey() : new byte[] {},
+ (crypt != null) ? crypt.getTruncationLengthBits() : 0,
+ (authCrypt != null) ? authCrypt.getName() : "",
+ (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
+ (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
+ encapType,
+ encapLocalPort,
+ encapRemotePort);
+ } catch (ServiceSpecificException e) {
+ // FIXME: get the error code and throw is at an IOException from Errno Exception
+ return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
}
// Both SAs were created successfully, time to construct a record and lock it away
userRecord.mTransformRecords.put(
resourceId,
new RefcountedResource<TransformRecord>(
- new TransformRecord(resourceId, c, spis, socketRecord),
+ new TransformRecord(resourceId, c, spiRecord, socketRecord),
binder,
dependencies.toArray(new RefcountedResource[dependencies.size()])));
return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
@@ -1217,9 +1197,9 @@
*/
@Override
public synchronized void applyTransportModeTransform(
- ParcelFileDescriptor socket, int resourceId) throws RemoteException {
+ ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
-
+ checkDirection(direction);
// Get transform record; if no transform is found, will throw IllegalArgumentException
TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
@@ -1230,17 +1210,15 @@
IpSecConfig c = info.getConfig();
try {
- for (int direction : DIRECTIONS) {
- mSrvConfig
- .getNetdInstance()
- .ipSecApplyTransportModeTransform(
- socket.getFileDescriptor(),
- resourceId,
- direction,
- c.getLocalAddress(),
- c.getRemoteAddress(),
- info.getSpiRecord(direction).getSpi());
- }
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecApplyTransportModeTransform(
+ socket.getFileDescriptor(),
+ resourceId,
+ direction,
+ c.getSourceAddress(),
+ c.getDestinationAddress(),
+ info.getSpiRecord().getSpi());
} catch (ServiceSpecificException e) {
if (e.errorCode == EINVAL) {
throw new IllegalArgumentException(e.toString());
@@ -1251,13 +1229,13 @@
}
/**
- * Remove a transport mode transform from a socket, applying the default (empty) policy. This
- * will ensure that NO IPsec policy is applied to the socket (would be the equivalent of
- * applying a policy that performs no IPsec). Today the resourceId parameter is passed but not
- * used: reserved for future improved input validation.
+ * Remove transport mode transforms from a socket, applying the default (empty) policy. This
+ * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
+ * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
+ * reserved for future improved input validation.
*/
@Override
- public synchronized void removeTransportModeTransform(ParcelFileDescriptor socket, int resourceId)
+ public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
throws RemoteException {
try {
mSrvConfig
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 8a15ded..88ae224 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -18,11 +18,9 @@
import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.DUMP;
+import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.SHUTDOWN;
-import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE;
-import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
-import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_NONE;
@@ -209,12 +207,6 @@
public static final int StrictCleartext = 617;
}
- /* Defaults for resolver parameters. */
- public static final int DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800;
- public static final int DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT = 25;
- public static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
- public static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
-
/**
* String indicating a softap command.
*/
@@ -1768,6 +1760,8 @@
@Override
public boolean setDataSaverModeEnabled(boolean enable) {
+ mContext.enforceCallingOrSelfPermission(NETWORK_SETTINGS, TAG);
+
if (DBG) Log.d(TAG, "setDataSaverMode: " + enable);
synchronized (mQuotaLock) {
if (mDataSaverMode == enable) {
@@ -1947,80 +1941,19 @@
}
@Override
- public void setDnsConfigurationForNetwork(int netId, String[] servers, String domains) {
+ public void setDnsConfigurationForNetwork(int netId, String[] servers, String[] domains,
+ int[] params, boolean useTls, String tlsHostname) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- final ContentResolver cr = mContext.getContentResolver();
-
- int sampleValidity = Settings.Global.getInt(cr,
- Settings.Global.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS,
- DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
- if (sampleValidity < 0 || sampleValidity > 65535) {
- Slog.w(TAG, "Invalid sampleValidity=" + sampleValidity + ", using default=" +
- DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
- sampleValidity = DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS;
- }
-
- int successThreshold = Settings.Global.getInt(cr,
- Settings.Global.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT,
- DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
- if (successThreshold < 0 || successThreshold > 100) {
- Slog.w(TAG, "Invalid successThreshold=" + successThreshold + ", using default=" +
- DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
- successThreshold = DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT;
- }
-
- int minSamples = Settings.Global.getInt(cr,
- Settings.Global.DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES);
- int maxSamples = Settings.Global.getInt(cr,
- Settings.Global.DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES);
- if (minSamples < 0 || minSamples > maxSamples || maxSamples > 64) {
- Slog.w(TAG, "Invalid sample count (min, max)=(" + minSamples + ", " + maxSamples +
- "), using default=(" + DNS_RESOLVER_DEFAULT_MIN_SAMPLES + ", " +
- DNS_RESOLVER_DEFAULT_MAX_SAMPLES + ")");
- minSamples = DNS_RESOLVER_DEFAULT_MIN_SAMPLES;
- maxSamples = DNS_RESOLVER_DEFAULT_MAX_SAMPLES;
- }
-
- final String[] domainStrs = domains == null ? new String[0] : domains.split(" ");
- final int[] params = { sampleValidity, successThreshold, minSamples, maxSamples };
- final boolean useTls = shouldUseTls(cr);
- // TODO: Populate tlsHostname once it's decided how the hostname's IP
- // addresses will be resolved:
- //
- // [1] network-provided DNS servers are included here with the
- // hostname and netd will use the network-provided servers to
- // resolve the hostname and fix up its internal structures, or
- //
- // [2] network-provided DNS servers are included here without the
- // hostname, the ConnectivityService layer resolves the given
- // hostname, and then reconfigures netd with this information.
- //
- // In practice, there will always be a need for ConnectivityService or
- // the captive portal app to use the network-provided services to make
- // some queries. This argues in favor of [1], in concert with another
- // mechanism, perhaps setting a high bit in the netid, to indicate
- // via existing DNS APIs which set of servers (network-provided or
- // non-network-provided private DNS) should be queried.
- final String tlsHostname = "";
final String[] tlsFingerprints = new String[0];
try {
- mNetdService.setResolverConfiguration(netId, servers, domainStrs, params,
- useTls, tlsHostname, tlsFingerprints);
+ mNetdService.setResolverConfiguration(
+ netId, servers, domains, params, useTls, tlsHostname, tlsFingerprints);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
- private static boolean shouldUseTls(ContentResolver cr) {
- String privateDns = Settings.Global.getString(cr, Settings.Global.PRIVATE_DNS_MODE);
- if (TextUtils.isEmpty(privateDns)) {
- privateDns = PRIVATE_DNS_DEFAULT_MODE;
- }
- return privateDns.equals(PRIVATE_DNS_MODE_OPPORTUNISTIC) ||
- privateDns.startsWith(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
- }
-
@Override
public void addVpnUidRanges(int netId, UidRange[] ranges) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
@@ -2563,12 +2496,16 @@
@Override
public void removeNetwork(int netId) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ mContext.enforceCallingOrSelfPermission(NETWORK_STACK, TAG);
try {
- mConnector.execute("network", "destroy", netId);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.networkDestroy(netId);
+ } catch (ServiceSpecificException e) {
+ Log.w(TAG, "removeNetwork(" + netId + "): ", e);
+ throw e;
+ } catch (RemoteException e) {
+ Log.w(TAG, "removeNetwork(" + netId + "): ", e);
+ throw e.rethrowAsRuntimeException();
}
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index dc2f2a5..7361e70 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -49,6 +49,7 @@
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.content.res.ObbInfo;
+import android.database.ContentObserver;
import android.net.TrafficStats;
import android.net.Uri;
import android.os.Binder;
@@ -170,6 +171,10 @@
// Static direct instance pointer for the tightly-coupled idle service to use
static StorageManagerService sSelf = null;
+ /* Read during boot to decide whether to enable zram when available */
+ private static final String ZRAM_ENABLED_PROPERTY =
+ "persist.sys.zram_enabled";
+
public static class Lifecycle extends SystemService {
private StorageManagerService mStorageManagerService;
@@ -733,6 +738,41 @@
// Start scheduling nominally-daily fstrim operations
MountServiceIdler.scheduleIdlePass(mContext);
+
+ // Toggle zram-enable system property in response to settings
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.ZRAM_ENABLED),
+ false /*notifyForDescendants*/,
+ new ContentObserver(null /* current thread */) {
+ @Override
+ public void onChange(boolean selfChange) {
+ refreshZramSettings();
+ }
+ });
+ refreshZramSettings();
+ }
+
+ /**
+ * Update the zram_enabled system property (which init reads to
+ * decide whether to enable zram) to reflect the zram_enabled
+ * preference (which we can change for experimentation purposes).
+ */
+ private void refreshZramSettings() {
+ String propertyValue = SystemProperties.get(ZRAM_ENABLED_PROPERTY);
+ if ("".equals(propertyValue)) {
+ return; // System doesn't have zram toggling support
+ }
+ String desiredPropertyValue =
+ Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.ZRAM_ENABLED,
+ 1) != 0
+ ? "1" : "0";
+ if (!desiredPropertyValue.equals(propertyValue)) {
+ // Avoid redundant disk writes by setting only if we're
+ // changing the property value. There's no race: we're the
+ // sole writer.
+ SystemProperties.set(ZRAM_ENABLED_PROPERTY, desiredPropertyValue);
+ }
}
/**
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 831c9cb..6747be3 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -147,6 +147,8 @@
private int[] mDataActivationState;
+ private boolean[] mUserMobileDataState;
+
private SignalStrength[] mSignalStrength;
private boolean[] mMessageWaiting;
@@ -304,6 +306,7 @@
mServiceState = new ServiceState[numPhones];
mVoiceActivationState = new int[numPhones];
mDataActivationState = new int[numPhones];
+ mUserMobileDataState = new boolean[numPhones];
mSignalStrength = new SignalStrength[numPhones];
mMessageWaiting = new boolean[numPhones];
mCallForwarding = new boolean[numPhones];
@@ -320,6 +323,7 @@
mCallIncomingNumber[i] = "";
mServiceState[i] = new ServiceState();
mSignalStrength[i] = new SignalStrength();
+ mUserMobileDataState[i] = false;
mMessageWaiting[i] = false;
mCallForwarding[i] = false;
mCellLocation[i] = new Bundle();
@@ -656,6 +660,13 @@
remove(r.binder);
}
}
+ if ((events & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) {
+ try {
+ r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
}
}
} else {
@@ -1012,6 +1023,33 @@
}
}
+ public void notifyUserMobileDataStateChangedForPhoneId(int phoneId, int subId, boolean state) {
+ if (!checkNotifyPermission("notifyUserMobileDataStateChanged()")) {
+ return;
+ }
+ if (VDBG) {
+ log("notifyUserMobileDataStateChangedForSubscriberPhoneID: subId=" + phoneId
+ + " state=" + state);
+ }
+ synchronized (mRecords) {
+ if (validatePhoneId(phoneId)) {
+ mMessageWaiting[phoneId] = state;
+ for (Record r : mRecords) {
+ if (r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) &&
+ idMatch(r.subId, subId, phoneId)) {
+ try {
+ r.callback.onUserMobileDataStateChanged(state);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
public void notifyCallForwardingChanged(boolean cfi) {
notifyCallForwardingChangedForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, cfi);
}
@@ -1374,6 +1412,7 @@
pw.println("mServiceState=" + mServiceState[i]);
pw.println("mVoiceActivationState= " + mVoiceActivationState[i]);
pw.println("mDataActivationState= " + mDataActivationState[i]);
+ pw.println("mUserMobileDataState= " + mUserMobileDataState[i]);
pw.println("mSignalStrength=" + mSignalStrength[i]);
pw.println("mMessageWaiting=" + mMessageWaiting[i]);
pw.println("mCallForwarding=" + mCallForwarding[i]);
@@ -1755,6 +1794,18 @@
}
}
+ if ((events & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) {
+ try {
+ if (VDBG) {
+ log("checkPossibleMissNotify: onUserMobileDataStateChanged phoneId="
+ + phoneId + " umds=" + mUserMobileDataState[phoneId]);
+ }
+ r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+
if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
try {
if (VDBG) {
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 35f83e4..30432df4 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -84,6 +84,7 @@
"/system/bin/sdcard",
"/system/bin/surfaceflinger",
"media.extractor", // system/bin/mediaextractor
+ "media.metrics", // system/bin/mediametrics
"media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service
"com.android.bluetooth", // Bluetooth service
};
diff --git a/services/core/java/com/android/server/am/LaunchingActivityPositioner.java b/services/core/java/com/android/server/am/ActivityLaunchParamsModifier.java
similarity index 68%
rename from services/core/java/com/android/server/am/LaunchingActivityPositioner.java
rename to services/core/java/com/android/server/am/ActivityLaunchParamsModifier.java
index 793884d..f44ee7a 100644
--- a/services/core/java/com/android/server/am/LaunchingActivityPositioner.java
+++ b/services/core/java/com/android/server/am/ActivityLaunchParamsModifier.java
@@ -19,23 +19,25 @@
import android.app.ActivityOptions;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
-import com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner;
+
+import com.android.server.am.LaunchParamsController.LaunchParams;
+import com.android.server.am.LaunchParamsController.LaunchParamsModifier;
/**
- * An implementation of {@link LaunchingBoundsPositioner}, which applies the launch bounds specified
+ * An implementation of {@link LaunchParamsModifier}, which applies the launch bounds specified
* inside {@link ActivityOptions#getLaunchBounds()}.
*/
-public class LaunchingActivityPositioner implements LaunchingBoundsPositioner {
+public class ActivityLaunchParamsModifier implements LaunchParamsModifier {
private final ActivityStackSupervisor mSupervisor;
- LaunchingActivityPositioner(ActivityStackSupervisor activityStackSupervisor) {
+ ActivityLaunchParamsModifier(ActivityStackSupervisor activityStackSupervisor) {
mSupervisor = activityStackSupervisor;
}
@Override
- public int onCalculateBounds(TaskRecord task, ActivityInfo.WindowLayout layout,
- ActivityRecord activity, ActivityRecord source,
- ActivityOptions options, Rect current, Rect result) {
+ public int onCalculate(TaskRecord task, ActivityInfo.WindowLayout layout,
+ ActivityRecord activity, ActivityRecord source, ActivityOptions options,
+ LaunchParams currentParams, LaunchParams outParams) {
// We only care about figuring out bounds for activities.
if (activity == null) {
return RESULT_SKIP;
@@ -43,7 +45,7 @@
// Activity must be resizeable in the specified task.
if (!(mSupervisor.canUseActivityOptionsLaunchBounds(options)
- && (activity.isResizeable() || (task != null && task.isResizeable())))) {
+ && (activity.isResizeable() || (task != null && task.isResizeable())))) {
return RESULT_SKIP;
}
@@ -54,7 +56,7 @@
return RESULT_SKIP;
}
- result.set(bounds);
+ outParams.mBounds.set(bounds);
// When this is the most explicit position specification so we should not allow further
// modification of the position.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6565187..48c678e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.BIND_VOICE_INTERACTION;
import static android.Manifest.permission.CHANGE_CONFIGURATION;
import static android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST;
+import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
@@ -192,11 +193,11 @@
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
-import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
-import static com.android.server.wm.AppTransition.TRANSIT_NONE;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_IN_PLACE;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -370,6 +371,7 @@
import android.util.proto.ProtoOutputStream;
import android.view.Gravity;
import android.view.LayoutInflater;
+import android.view.RemoteAnimationDefinition;
import android.view.View;
import android.view.WindowManager;
@@ -397,6 +399,7 @@
import com.android.internal.os.TransferPipe;
import com.android.internal.os.Zygote;
import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.policy.KeyguardDismissCallback;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
@@ -6242,7 +6245,7 @@
// Clear its pending alarms
AlarmManagerInternal ami = LocalServices.getService(AlarmManagerInternal.class);
- ami.removeAlarmsForUid(uid);
+ ami.removeAlarmsForUid(appInfo.uid);
}
} catch (RemoteException e) {
}
@@ -7260,15 +7263,22 @@
}
ProfilerInfo profilerInfo = null;
- String agent = null;
+ String preBindAgent = null;
if (mProfileApp != null && mProfileApp.equals(processName)) {
mProfileProc = app;
- profilerInfo = (mProfilerInfo != null && mProfilerInfo.profileFile != null) ?
- new ProfilerInfo(mProfilerInfo) : null;
- agent = mProfilerInfo != null ? mProfilerInfo.agent : null;
+ if (mProfilerInfo != null) {
+ // Send a profiler info object to the app if either a file is given, or
+ // an agent should be loaded at bind-time.
+ boolean needsInfo = mProfilerInfo.profileFile != null
+ || mProfilerInfo.attachAgentDuringBind;
+ profilerInfo = needsInfo ? new ProfilerInfo(mProfilerInfo) : null;
+ if (!mProfilerInfo.attachAgentDuringBind) {
+ preBindAgent = mProfilerInfo.agent;
+ }
+ }
} else if (app.instr != null && app.instr.mProfileFile != null) {
profilerInfo = new ProfilerInfo(app.instr.mProfileFile, null, 0, false, false,
- null);
+ null, false);
}
boolean enableTrackAllocation = false;
@@ -7337,8 +7347,8 @@
// If we were asked to attach an agent on startup, do so now, before we're binding
// application code.
- if (agent != null) {
- thread.attachAgent(agent);
+ if (preBindAgent != null) {
+ thread.attachAgent(preBindAgent);
}
checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
@@ -8386,22 +8396,12 @@
// entering picture-in-picture (this will prompt the user to authenticate if the
// device is currently locked).
try {
- dismissKeyguard(token, new IKeyguardDismissCallback.Stub() {
- @Override
- public void onDismissError() throws RemoteException {
- // Do nothing
- }
-
+ dismissKeyguard(token, new KeyguardDismissCallback() {
@Override
public void onDismissSucceeded() throws RemoteException {
mHandler.post(enterPipRunnable);
}
-
- @Override
- public void onDismissCancelled() throws RemoteException {
- // Do nothing
- }
- });
+ }, null /* message */);
} catch (RemoteException e) {
// Local call
}
@@ -13676,7 +13676,8 @@
* not.
*/
private void enforceSystemHasVrFeature() {
- if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
+ if (!mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) {
throw new UnsupportedOperationException("VR mode not supported on this device!");
}
}
@@ -13735,9 +13736,7 @@
@Override
public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
- if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
- throw new UnsupportedOperationException("VR mode not supported on this device!");
- }
+ enforceSystemHasVrFeature();
final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
@@ -13769,9 +13768,7 @@
@Override
public boolean isVrModePackageEnabled(ComponentName packageName) {
- if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
- throw new UnsupportedOperationException("VR mode not supported on this device!");
- }
+ enforceSystemHasVrFeature();
final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
@@ -25130,6 +25127,21 @@
public void registerScreenObserver(ScreenObserver observer) {
mScreenObservers.add(observer);
}
+
+ @Override
+ public boolean canStartMoreUsers() {
+ return mUserController.canStartMoreUsers();
+ }
+
+ @Override
+ public void setSwitchingFromSystemUserMessage(String switchingFromSystemUserMessage) {
+ mUserController.setSwitchingFromSystemUserMessage(switchingFromSystemUserMessage);
+ }
+
+ @Override
+ public void setSwitchingToSystemUserMessage(String switchingToSystemUserMessage) {
+ mUserController.setSwitchingToSystemUserMessage(switchingToSystemUserMessage);
+ }
}
/**
@@ -25277,11 +25289,15 @@
}
@Override
- public void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback)
- throws RemoteException {
+ public void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback,
+ CharSequence message) throws RemoteException {
+ if (message != null) {
+ enforceCallingPermission(permission.SHOW_KEYGUARD_MESSAGE,
+ "dismissKeyguard()");
+ }
final long callingId = Binder.clearCallingIdentity();
try {
- mKeyguardController.dismissKeyguard(token, callback);
+ mKeyguardController.dismissKeyguard(token, callback, message);
} finally {
Binder.restoreCallingIdentity(callingId);
}
@@ -25336,6 +25352,10 @@
}
}
}
+ if (updateFrameworkRes && mWindowManager != null) {
+ ActivityThread.currentActivityThread().getExecutor().execute(
+ mWindowManager::onOverlayChanged);
+ }
}
/**
@@ -25426,4 +25446,23 @@
}
}
}
+
+ @Override
+ public void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition)
+ throws RemoteException {
+ enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+ "registerRemoteAnimations");
+ synchronized (this) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ return;
+ }
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ r.registerRemoteAnimations(definition);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 4f60e17..1240f5e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -109,6 +109,7 @@
private boolean mAutoStop;
private boolean mStreaming; // Streaming the profiling output to a file.
private String mAgent; // Agent to attach on startup.
+ private boolean mAttachAgentDuringBind; // Whether agent should be attached late.
private int mDisplayId;
private int mWindowingMode;
private int mActivityType;
@@ -296,7 +297,21 @@
} else if (opt.equals("--streaming")) {
mStreaming = true;
} else if (opt.equals("--attach-agent")) {
+ if (mAgent != null) {
+ cmd.getErrPrintWriter().println(
+ "Multiple --attach-agent(-bind) not supported");
+ return false;
+ }
mAgent = getNextArgRequired();
+ mAttachAgentDuringBind = false;
+ } else if (opt.equals("--attach-agent-bind")) {
+ if (mAgent != null) {
+ cmd.getErrPrintWriter().println(
+ "Multiple --attach-agent(-bind) not supported");
+ return false;
+ }
+ mAgent = getNextArgRequired();
+ mAttachAgentDuringBind = true;
} else if (opt.equals("-R")) {
mRepeat = Integer.parseInt(getNextArgRequired());
} else if (opt.equals("-S")) {
@@ -384,7 +399,7 @@
}
}
profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop,
- mStreaming, mAgent);
+ mStreaming, mAgent, mAttachAgentDuringBind);
}
pw.println("Starting: " + intent);
@@ -766,7 +781,7 @@
return -1;
}
profilerInfo = new ProfilerInfo(profileFile, fd, mSamplingInterval, false, mStreaming,
- null);
+ null, false);
}
try {
@@ -2544,6 +2559,7 @@
pw.println(" (use with --start-profiler)");
pw.println(" -P <FILE>: like above, but profiling stops when app goes idle");
pw.println(" --attach-agent <agent>: attach the given agent before binding");
+ pw.println(" --attach-agent-bind <agent>: attach the given agent during binding");
pw.println(" -R: repeat the activity launch <COUNT> times. Prior to each repeat,");
pw.println(" the top activity will be finished.");
pw.println(" -S: force stop the target app before starting the activity");
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index e1907d3..3bef877 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -21,6 +21,7 @@
import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
import static android.app.ActivityOptions.ANIM_SCALE_UP;
import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
@@ -135,6 +136,7 @@
import android.app.servertransaction.MoveToDisplayItem;
import android.app.servertransaction.MultiWindowModeChangeItem;
import android.app.servertransaction.NewIntentItem;
+import android.app.servertransaction.PauseActivityItem;
import android.app.servertransaction.PipModeChangeItem;
import android.app.servertransaction.WindowVisibilityItem;
import android.app.servertransaction.ActivityConfigurationChangeItem;
@@ -169,6 +171,7 @@
import android.view.AppTransitionAnimationSpec;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IApplicationToken;
+import android.view.RemoteAnimationDefinition;
import android.view.WindowManager.LayoutParams;
import com.android.internal.R;
@@ -371,6 +374,10 @@
}
}
+ String getLifecycleDescription(String reason) {
+ return "packageName=" + packageName + ", state=" + state + ", reason=" + reason;
+ }
+
void dump(PrintWriter pw, String prefix) {
final long now = SystemClock.uptimeMillis();
pw.print(prefix); pw.print("packageName="); pw.print(packageName);
@@ -1480,6 +1487,10 @@
case ANIM_OPEN_CROSS_PROFILE_APPS:
service.mWindowManager.overridePendingAppTransitionStartCrossProfileApps();
break;
+ case ANIM_REMOTE_ANIMATION:
+ service.mWindowManager.overridePendingAppTransitionRemote(
+ pendingOptions.getRemoteAnimationAdapter());
+ break;
default:
Slog.e(TAG, "applyOptionsLocked: Unknown animationType=" + animationType);
break;
@@ -1607,6 +1618,20 @@
// The activity may be waiting for stop, but that is no longer appropriate for it.
mStackSupervisor.mStoppingActivities.remove(this);
mStackSupervisor.mGoingToSleepActivities.remove(this);
+
+ // If the activity is stopped or stopping, cycle to the paused state.
+ if (state == STOPPED || state == STOPPING) {
+ // Capture reason before state change
+ final String reason = getLifecycleDescription("makeVisibleIfNeeded");
+
+ // An activity must be in the {@link PAUSING} state for the system to validate
+ // the move to {@link PAUSED}.
+ state = PAUSING;
+ service.mLifecycleManager.scheduleTransaction(app.thread, appToken,
+ PauseActivityItem.obtain(finishing, false /* userLeaving */,
+ configChangeFlags, false /* dontReport */)
+ .setDescription(reason));
+ }
} catch (Exception e) {
// Just skip on any failure; we'll make it visible when it next restarts.
Slog.w(TAG, "Exception thrown making visibile: " + intent.getComponent(), e);
@@ -2766,6 +2791,10 @@
return mStackSupervisor.topRunningActivityLocked() == this;
}
+ void registerRemoteAnimations(RemoteAnimationDefinition definition) {
+ mWindowContainerController.registerRemoteAnimations(definition);
+ }
+
@Override
public String toString() {
if (stringName != null) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 831b31e..0904c8e 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -84,14 +84,14 @@
import static com.android.server.am.proto.ActivityStackProto.ID;
import static com.android.server.am.proto.ActivityStackProto.RESUMED_ACTIVITY;
import static com.android.server.am.proto.ActivityStackProto.TASKS;
-import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_CLOSE;
-import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
-import static com.android.server.wm.AppTransition.TRANSIT_NONE;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_CLOSE;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN_BEHIND;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_BACK;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
+import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
import static java.lang.Integer.MAX_VALUE;
@@ -2639,7 +2639,9 @@
next.clearOptionsLocked();
mService.mLifecycleManager.scheduleTransaction(next.app.thread, next.appToken,
ResumeActivityItem.obtain(next.app.repProcState,
- mService.isNextTransitionForward()));
+ mService.isNextTransitionForward())
+ .setDescription(next.getLifecycleDescription(
+ "resumeTopActivityInnerLocked")));
if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed "
+ next);
@@ -3419,7 +3421,8 @@
EventLogTags.writeAmStopActivity(
r.userId, System.identityHashCode(r), r.shortComponentName);
mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken,
- StopActivityItem.obtain(r.visible, r.configChangeFlags));
+ StopActivityItem.obtain(r.visible, r.configChangeFlags)
+ .setDescription(r.getLifecycleDescription("stopActivityLocked")));
if (shouldSleepOrShutDownActivities()) {
r.setSleeping(true);
}
@@ -4225,7 +4228,8 @@
try {
if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + r);
mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken,
- DestroyActivityItem.obtain(r.finishing, r.configChangeFlags));
+ DestroyActivityItem.obtain(r.finishing, r.configChangeFlags)
+ .setDescription(r.getLifecycleDescription("destroyActivityLocked")));
} catch (Exception e) {
// We can just ignore exceptions here... if the process
// has crashed, our death notification will clean things
@@ -5052,7 +5056,7 @@
addTask(task, toTop, "createTaskRecord");
final boolean isLockscreenShown = mService.mStackSupervisor.getKeyguardController()
.isKeyguardShowing(mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY);
- if (!mStackSupervisor.getLaunchingBoundsController()
+ if (!mStackSupervisor.getLaunchParamsController()
.layoutTask(task, info.windowLayout, activity, source, options)
&& !matchParentBounds() && task.isResizeable() && !isLockscreenShown) {
task.updateOverrideConfiguration(getOverrideBounds());
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 21085fa..8168cba 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import static android.Manifest.permission.ACTIVITY_EMBEDDING;
+import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.START_ANY_ACTIVITY;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
@@ -93,7 +94,7 @@
import static com.android.server.am.proto.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
import static com.android.server.am.proto.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
import static com.android.server.am.proto.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
-import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS;
+import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
import static java.lang.Integer.MAX_VALUE;
@@ -162,6 +163,7 @@
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
+import android.view.RemoteAnimationAdapter;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.ReferrerIntent;
@@ -300,7 +302,7 @@
WindowManagerService mWindowManager;
DisplayManager mDisplayManager;
- private LaunchingBoundsController mLaunchingBoundsController;
+ private LaunchParamsController mLaunchParamsController;
/**
* Maps the task identifier that activities are currently being started in to the userId of the
@@ -593,8 +595,8 @@
mHandler.getLooper());
mKeyguardController = new KeyguardController(mService, this);
- mLaunchingBoundsController = new LaunchingBoundsController();
- mLaunchingBoundsController.registerDefaultPositioners(this);
+ mLaunchParamsController = new LaunchParamsController(mService);
+ mLaunchParamsController.registerDefaultModifiers(this);
}
@@ -1422,7 +1424,8 @@
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
- lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
+ lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward())
+ .setDescription(r.getLifecycleDescription("realStartActivityLocked"));
} else {
lifecycleItem = PauseActivityItem.obtain();
}
@@ -1680,6 +1683,18 @@
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
+
+ // Check permission for remote animations
+ final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
+ if (adapter != null && mService.checkPermission(
+ CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid)
+ != PERMISSION_GRANTED) {
+ final String msg = "Permission Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ") with remoteAnimationAdapter";
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
}
return true;
@@ -2220,8 +2235,8 @@
|| mService.mSupportsFreeformWindowManagement;
}
- LaunchingBoundsController getLaunchingBoundsController() {
- return mLaunchingBoundsController;
+ LaunchParamsController getLaunchParamsController() {
+ return mLaunchParamsController;
}
protected <T extends ActivityStack> T getStack(int stackId) {
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 6684f25..0480646 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -30,6 +30,7 @@
import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
import android.app.ActivityOptions;
+import android.app.AppGlobals;
import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Context;
@@ -40,10 +41,12 @@
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.os.Binder;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.HarmfulAppWarningActivity;
import com.android.internal.app.UnlaunchableAppActivity;
import com.android.server.LocalServices;
@@ -115,6 +118,15 @@
mCallingPackage = callingPackage;
}
+ private IntentSender createIntentSenderForOriginalIntent(int callingUid, int flags) {
+ final IIntentSender target = mService.getIntentSenderLocked(
+ INTENT_SENDER_ACTIVITY, mCallingPackage, callingUid, mUserId, null /*token*/,
+ null /*resultCode*/, 0 /*requestCode*/,
+ new Intent[] { mIntent }, new String[] { mResolvedType },
+ flags, null /*bOptions*/);
+ return new IntentSender(target);
+ }
+
/**
* Intercept the launch intent based on various signals. If an interception happened the
* internal variables get assigned and need to be read explicitly by the caller.
@@ -144,6 +156,11 @@
// be unlocked when profile's user is running.
return true;
}
+ if (interceptHarmfulAppIfNeeded()) {
+ // If the app has a "harmful app" warning associated with it, we should ask to uninstall
+ // before issuing the work challenge.
+ return true;
+ }
return interceptWorkProfileChallengeIfNeeded();
}
@@ -152,13 +169,10 @@
if (!mUserManager.isQuietModeEnabled(UserHandle.of(mUserId))) {
return false;
}
- IIntentSender target = mService.getIntentSenderLocked(
- INTENT_SENDER_ACTIVITY, mCallingPackage, mCallingUid, mUserId, null, null, 0,
- new Intent[] {mIntent}, new String[] {mResolvedType},
- FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT, null);
+ IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
+ FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT);
- mIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(mUserId,
- new IntentSender(target));
+ mIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(mUserId, target);
mCallingPid = mRealCallingPid;
mCallingUid = mRealCallingUid;
mResolvedType = null;
@@ -240,11 +254,8 @@
return null;
}
// TODO(b/28935539): should allow certain activities to bypass work challenge
- final IIntentSender target = mService.getIntentSenderLocked(
- INTENT_SENDER_ACTIVITY, callingPackage,
- Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
- new String[]{ resolvedType },
- FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE, null);
+ final IntentSender target = createIntentSenderForOriginalIntent(Binder.getCallingUid(),
+ FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE);
final KeyguardManager km = (KeyguardManager) mServiceContext
.getSystemService(KEYGUARD_SERVICE);
final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, userId);
@@ -254,8 +265,36 @@
newIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
FLAG_ACTIVITY_TASK_ON_HOME);
newIntent.putExtra(EXTRA_PACKAGE_NAME, aInfo.packageName);
- newIntent.putExtra(EXTRA_INTENT, new IntentSender(target));
+ newIntent.putExtra(EXTRA_INTENT, target);
return newIntent;
}
+ private boolean interceptHarmfulAppIfNeeded() {
+ CharSequence harmfulAppWarning;
+ try {
+ harmfulAppWarning = AppGlobals.getPackageManager().getHarmfulAppWarning(
+ mAInfo.packageName, mUserId);
+ } catch (RemoteException e) {
+ return false;
+ }
+
+ if (harmfulAppWarning == null) {
+ return false;
+ }
+
+ final IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
+ FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE);
+
+ mIntent = HarmfulAppWarningActivity.createHarmfulAppWarningIntent(mServiceContext,
+ mAInfo.packageName, target, harmfulAppWarning);
+
+ mCallingPid = mRealCallingPid;
+ mCallingUid = mRealCallingUid;
+ mResolvedType = null;
+
+ mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId);
+ mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
+ return true;
+ }
+
}
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index c98e337..3a13155 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -96,7 +96,6 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.voice.IVoiceInteractionSession;
@@ -109,6 +108,7 @@
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.app.IVoiceInteractor;
import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch;
+import com.android.server.am.LaunchParamsController.LaunchParams;
import com.android.server.pm.InstantAppResolver;
import java.io.PrintWriter;
@@ -144,7 +144,7 @@
private boolean mLaunchTaskBehind;
private int mLaunchFlags;
- private Rect mLaunchBounds = new Rect();
+ private LaunchParams mLaunchParams = new LaunchParams();
private ActivityRecord mNotTop;
private boolean mDoResume;
@@ -419,7 +419,7 @@
mLaunchFlags = starter.mLaunchFlags;
mLaunchMode = starter.mLaunchMode;
- mLaunchBounds.set(starter.mLaunchBounds);
+ mLaunchParams.set(starter.mLaunchParams);
mNotTop = starter.mNotTop;
mDoResume = starter.mDoResume;
@@ -1154,6 +1154,18 @@
preferredLaunchDisplayId = mOptions.getLaunchDisplayId();
}
+ // windowing mode and preferred launch display values from {@link LaunchParams} take
+ // priority over those specified in {@link ActivityOptions}.
+ if (!mLaunchParams.isEmpty()) {
+ if (mLaunchParams.hasPreferredDisplay()) {
+ preferredLaunchDisplayId = mLaunchParams.mPreferredDisplayId;
+ }
+
+ if (mLaunchParams.hasWindowingMode()) {
+ preferredWindowingMode = mLaunchParams.mWindowingMode;
+ }
+ }
+
if (reusedActivity != null) {
// When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
// still needs to be a lock task mode violation since the task gets cleared out and
@@ -1378,7 +1390,7 @@
mLaunchFlags = 0;
mLaunchMode = INVALID_LAUNCH_MODE;
- mLaunchBounds.setEmpty();
+ mLaunchParams.reset();
mNotTop = null;
mDoResume = false;
@@ -1425,10 +1437,10 @@
mPreferredDisplayId = getPreferedDisplayId(mSourceRecord, mStartActivity, options);
- mLaunchBounds.setEmpty();
+ mLaunchParams.reset();
- mSupervisor.getLaunchingBoundsController().calculateBounds(inTask, null /*layout*/, r,
- sourceRecord, options, mLaunchBounds);
+ mSupervisor.getLaunchParamsController().calculate(inTask, null /*layout*/, r, sourceRecord,
+ options, mLaunchParams);
mLaunchMode = r.launchMode;
@@ -1938,7 +1950,7 @@
mVoiceInteractor, !mLaunchTaskBehind /* toTop */, mStartActivity, mSourceRecord,
mOptions);
addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
- updateBounds(mStartActivity.getTask(), mLaunchBounds);
+ updateBounds(mStartActivity.getTask(), mLaunchParams.mBounds);
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
+ " in new task " + mStartActivity.getTask());
@@ -2102,7 +2114,7 @@
return START_TASK_TO_FRONT;
}
- if (!mLaunchBounds.isEmpty()) {
+ if (!mLaunchParams.mBounds.isEmpty()) {
// TODO: Shouldn't we already know what stack to use by the time we get here?
ActivityStack stack = mSupervisor.getLaunchStack(null, null, mInTask, ON_TOP);
if (stack != mInTask.getStack()) {
@@ -2111,7 +2123,7 @@
mTargetStack = mInTask.getStack();
}
- updateBounds(mInTask, mLaunchBounds);
+ updateBounds(mInTask, mLaunchParams.mBounds);
}
mTargetStack.moveTaskToFrontLocked(
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index c9aa9a2..81e8eb0 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -933,21 +933,6 @@
}
}
- public void noteWifiMulticastEnabledFromSource(WorkSource ws) {
- enforceCallingPermission();
- synchronized (mStats) {
- mStats.noteWifiMulticastEnabledFromSourceLocked(ws);
- }
- }
-
- @Override
- public void noteWifiMulticastDisabledFromSource(WorkSource ws) {
- enforceCallingPermission();
- synchronized (mStats) {
- mStats.noteWifiMulticastDisabledFromSourceLocked(ws);
- }
- }
-
@Override
public void noteNetworkInterfaceType(String iface, int networkType) {
enforceCallingPermission();
diff --git a/services/core/java/com/android/server/am/ClientLifecycleManager.java b/services/core/java/com/android/server/am/ClientLifecycleManager.java
index 014f708..ae8d9fc 100644
--- a/services/core/java/com/android/server/am/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/am/ClientLifecycleManager.java
@@ -21,6 +21,7 @@
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.ClientTransactionItem;
import android.app.servertransaction.ActivityLifecycleItem;
+import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -42,9 +43,14 @@
* @see ClientTransaction
*/
void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
+ final IApplicationThread client = transaction.getClient();
transaction.schedule();
- // TODO: b/70616950
- //transaction.recycle();
+ if (!(client instanceof Binder)) {
+ // If client is not an instance of Binder - it's a remote call and at this point it is
+ // safe to recycle the object. All objects used for local calls will be recycled after
+ // the transaction is executed on client in ActivityThread.
+ transaction.recycle();
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
index 5632fc0..c9afc17 100644
--- a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
@@ -37,7 +37,8 @@
private static final String[][] sGlobalSettingsMapping = new String[][] {
// List mapping entries in the following format:
- // {Settings.Global.SETTING_NAME, "system_property_name"},
+ // {Settings.Global.SETTING_NAME, "system_property_name"},
+ {Settings.Global.SYS_VDSO, "sys.vdso"},
};
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index 35f4f25..79f3fe3 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -27,13 +27,13 @@
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.am.proto.KeyguardControllerProto.KEYGUARD_OCCLUDED;
import static com.android.server.am.proto.KeyguardControllerProto.KEYGUARD_SHOWING;
-import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
-import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
-import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
-import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_GOING_AWAY;
-import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_OCCLUDE;
-import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_UNOCCLUDE;
-import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_UNSET;
import android.app.ActivityManagerInternal.SleepToken;
import android.os.IBinder;
@@ -150,7 +150,7 @@
}
}
- void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback) {
+ void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback, CharSequence message) {
final ActivityRecord activityRecord = ActivityRecord.forTokenLocked(token);
if (activityRecord == null || !activityRecord.visibleIgnoringKeyguard) {
failCallback(callback);
@@ -164,7 +164,7 @@
mStackSupervisor.wakeUp("dismissKeyguard");
}
- mWindowManager.dismissKeyguard(callback);
+ mWindowManager.dismissKeyguard(callback, message);
}
private void setKeyguardGoingAway(boolean keyguardGoingAway) {
@@ -304,7 +304,7 @@
// insecure case, we actually show it on top of the lockscreen. See #canShowWhileOccluded.
if (!mOccluded && mDismissingKeyguardActivity != null
&& mWindowManager.isKeyguardSecure()) {
- mWindowManager.dismissKeyguard(null /* callback */);
+ mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
mDismissalRequested = true;
// If we are about to unocclude the Keyguard, but we can dismiss it without security,
diff --git a/services/core/java/com/android/server/am/LaunchParamsController.java b/services/core/java/com/android/server/am/LaunchParamsController.java
new file mode 100644
index 0000000..7ab7f98
--- /dev/null
+++ b/services/core/java/com/android/server/am/LaunchParamsController.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2017 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 android.annotation.IntDef;
+import android.app.ActivityOptions;
+import android.content.pm.ActivityInfo.WindowLayout;
+import android.graphics.Rect;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
+import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
+import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
+
+/**
+ * {@link LaunchParamsController} calculates the {@link LaunchParams} by coordinating between
+ * registered {@link LaunchParamsModifier}s.
+ */
+class LaunchParamsController {
+ private final ActivityManagerService mService;
+ private final List<LaunchParamsModifier> mModifiers = new ArrayList<>();
+
+ // Temporary {@link LaunchParams} for internal calculations. This is kept separate from
+ // {@code mTmpCurrent} and {@code mTmpResult} to prevent clobbering values.
+ private final LaunchParams mTmpParams = new LaunchParams();
+
+ private final LaunchParams mTmpCurrent = new LaunchParams();
+ private final LaunchParams mTmpResult = new LaunchParams();
+
+ LaunchParamsController(ActivityManagerService service) {
+ mService = service;
+ }
+
+ /**
+ * Creates a {@link LaunchParamsController} with default registered
+ * {@link LaunchParamsModifier}s.
+ */
+ void registerDefaultModifiers(ActivityStackSupervisor supervisor) {
+ // {@link TaskLaunchParamsModifier} handles window layout preferences.
+ registerModifier(new TaskLaunchParamsModifier());
+
+ // {@link ActivityLaunchParamsModifier} is the most specific modifier and thus should be
+ // registered last (applied first) out of the defaults.
+ registerModifier(new ActivityLaunchParamsModifier(supervisor));
+ }
+
+ /**
+ * Returns the {@link LaunchParams} calculated by the registered modifiers
+ * @param task The {@link TaskRecord} currently being positioned.
+ * @param layout The specified {@link WindowLayout}.
+ * @param activity The {@link ActivityRecord} currently being positioned.
+ * @param source The {@link ActivityRecord} from which activity was started from.
+ * @param options The {@link ActivityOptions} specified for the activity.
+ * @param result The resulting params.
+ */
+ void calculate(TaskRecord task, WindowLayout layout, ActivityRecord activity,
+ ActivityRecord source, ActivityOptions options, LaunchParams result) {
+ result.reset();
+
+ // We start at the last registered {@link LaunchParamsModifier} as this represents
+ // The modifier closest to the product level. Moving back through the list moves closer to
+ // the platform logic.
+ for (int i = mModifiers.size() - 1; i >= 0; --i) {
+ mTmpCurrent.set(result);
+ mTmpResult.reset();
+ final LaunchParamsModifier modifier = mModifiers.get(i);
+
+ switch(modifier.onCalculate(task, layout, activity, source, options, mTmpCurrent,
+ mTmpResult)) {
+ case RESULT_SKIP:
+ // Do not apply any results when we are told to skip
+ continue;
+ case RESULT_DONE:
+ // Set result and return immediately.
+ result.set(mTmpResult);
+ return;
+ case RESULT_CONTINUE:
+ // Set result and continue
+ result.set(mTmpResult);
+ break;
+ }
+ }
+ }
+
+ /**
+ * A convenience method for laying out a task.
+ * @return {@code true} if bounds were set on the task. {@code false} otherwise.
+ */
+ boolean layoutTask(TaskRecord task, WindowLayout layout) {
+ return layoutTask(task, layout, null /*activity*/, null /*source*/, null /*options*/);
+ }
+
+ boolean layoutTask(TaskRecord task, WindowLayout layout, ActivityRecord activity,
+ ActivityRecord source, ActivityOptions options) {
+ calculate(task, layout, activity, source, options, mTmpParams);
+
+ // No changes, return.
+ if (mTmpParams.isEmpty()) {
+ return false;
+ }
+
+ mService.mWindowManager.deferSurfaceLayout();
+
+ try {
+ if (mTmpParams.hasPreferredDisplay()
+ && mTmpParams.mPreferredDisplayId != task.getStack().getDisplay().mDisplayId) {
+ mService.moveStackToDisplay(task.getStackId(), mTmpParams.mPreferredDisplayId);
+ }
+
+ if (mTmpParams.hasWindowingMode()
+ && mTmpParams.mWindowingMode != task.getStack().getWindowingMode()) {
+ task.getStack().setWindowingMode(mTmpParams.mWindowingMode);
+ }
+
+ if (!mTmpParams.mBounds.isEmpty()) {
+ task.updateOverrideConfiguration(mTmpParams.mBounds);
+ return true;
+ } else {
+ return false;
+ }
+ } finally {
+ mService.mWindowManager.continueSurfaceLayout();
+ }
+ }
+
+ /**
+ * Adds a modifier to participate in future bounds calculation. Note that the last registered
+ * {@link LaunchParamsModifier} will be the first to calculate the bounds.
+ */
+ void registerModifier(LaunchParamsModifier modifier) {
+ if (mModifiers.contains(modifier)) {
+ return;
+ }
+
+ mModifiers.add(modifier);
+ }
+
+ /**
+ * A container for holding launch related fields.
+ */
+ static class LaunchParams {
+ /** The bounds within the parent container. */
+ final Rect mBounds = new Rect();
+
+ /** The id of the display the {@link TaskRecord} would prefer to be on. */
+ int mPreferredDisplayId;
+
+ /** The windowing mode to be in. */
+ int mWindowingMode;
+
+ /** Sets values back to default. {@link #isEmpty} will return {@code true} once called. */
+ void reset() {
+ mBounds.setEmpty();
+ mPreferredDisplayId = INVALID_DISPLAY;
+ mWindowingMode = WINDOWING_MODE_UNDEFINED;
+ }
+
+ /** Copies the values set on the passed in {@link LaunchParams}. */
+ void set(LaunchParams params) {
+ mBounds.set(params.mBounds);
+ mPreferredDisplayId = params.mPreferredDisplayId;
+ mWindowingMode = params.mWindowingMode;
+ }
+
+ /** Returns {@code true} if no values have been explicitly set. */
+ boolean isEmpty() {
+ return mBounds.isEmpty() && mPreferredDisplayId == INVALID_DISPLAY
+ && mWindowingMode == WINDOWING_MODE_UNDEFINED;
+ }
+
+ boolean hasWindowingMode() {
+ return mWindowingMode != WINDOWING_MODE_UNDEFINED;
+ }
+
+ boolean hasPreferredDisplay() {
+ return mPreferredDisplayId != INVALID_DISPLAY;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ LaunchParams that = (LaunchParams) o;
+
+ if (mPreferredDisplayId != that.mPreferredDisplayId) return false;
+ if (mWindowingMode != that.mWindowingMode) return false;
+ return mBounds != null ? mBounds.equals(that.mBounds) : that.mBounds == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mBounds != null ? mBounds.hashCode() : 0;
+ result = 31 * result + mPreferredDisplayId;
+ result = 31 * result + mWindowingMode;
+ return result;
+ }
+ }
+
+ /**
+ * An interface implemented by those wanting to participate in bounds calculation.
+ */
+ interface LaunchParamsModifier {
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({RESULT_SKIP, RESULT_DONE, RESULT_CONTINUE})
+ @interface Result {}
+
+ // Returned when the modifier does not want to influence the bounds calculation
+ int RESULT_SKIP = 0;
+ // Returned when the modifier has changed the bounds and would like its results to be the
+ // final bounds applied.
+ int RESULT_DONE = 1;
+ // Returned when the modifier has changed the bounds but is okay with other modifiers
+ // influencing the bounds.
+ int RESULT_CONTINUE = 2;
+
+ /**
+ * Called when asked to calculate {@link LaunchParams}.
+ * @param task The {@link TaskRecord} currently being positioned.
+ * @param layout The specified {@link WindowLayout}.
+ * @param activity The {@link ActivityRecord} currently being positioned.
+ * @param source The {@link ActivityRecord} activity was started from.
+ * @param options The {@link ActivityOptions} specified for the activity.
+ * @param currentParams The current {@link LaunchParams}. This can differ from the initial
+ * params as it represents the modified params up to this point.
+ * @param outParams The resulting {@link LaunchParams} after all calculations.
+ * @return A {@link Result} representing the result of the
+ * {@link LaunchParams} calculation.
+ */
+ @Result
+ int onCalculate(TaskRecord task, WindowLayout layout, ActivityRecord activity,
+ ActivityRecord source, ActivityOptions options, LaunchParams currentParams,
+ LaunchParams outParams);
+ }
+}
diff --git a/services/core/java/com/android/server/am/LaunchingBoundsController.java b/services/core/java/com/android/server/am/LaunchingBoundsController.java
deleted file mode 100644
index 5aa7f58..0000000
--- a/services/core/java/com/android/server/am/LaunchingBoundsController.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2017 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 android.annotation.IntDef;
-import android.app.ActivityOptions;
-import android.content.pm.ActivityInfo.WindowLayout;
-import android.graphics.Rect;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-
-import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_CONTINUE;
-import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_DONE;
-import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_SKIP;
-
-/**
- * {@link LaunchingBoundsController} calculates the launch bounds by coordinating between registered
- * {@link LaunchingBoundsPositioner}.
- */
-class LaunchingBoundsController {
- private final List<LaunchingBoundsPositioner> mPositioners = new ArrayList<>();
-
- // Temporary {@link Rect} for calculations. This is kept separate from {@code mTmpCurrent} and
- // {@code mTmpResult} to prevent clobbering values.
- private final Rect mTmpRect = new Rect();
-
- private final Rect mTmpCurrent = new Rect();
- private final Rect mTmpResult = new Rect();
-
- /**
- * Creates a {@link LaunchingBoundsController} with default registered
- * {@link LaunchingBoundsPositioner}s.
- */
- void registerDefaultPositioners(ActivityStackSupervisor supervisor) {
- // {@link LaunchingTaskPositioner} handles window layout preferences.
- registerPositioner(new LaunchingTaskPositioner());
-
- // {@link LaunchingActivityPositioner} is the most specific positioner and thus should be
- // registered last (applied first) out of the defaults.
- registerPositioner(new LaunchingActivityPositioner(supervisor));
- }
-
- /**
- * Returns the position calculated by the registered positioners
- * @param task The {@link TaskRecord} currently being positioned.
- * @param layout The specified {@link WindowLayout}.
- * @param activity The {@link ActivityRecord} currently being positioned.
- * @param source The {@link ActivityRecord} from which activity was started from.
- * @param options The {@link ActivityOptions} specified for the activity.
- * @param result The resulting bounds. If no bounds are set, {@link Rect#isEmpty()} will be
- * {@code true}.
- */
- void calculateBounds(TaskRecord task, WindowLayout layout, ActivityRecord activity,
- ActivityRecord source, ActivityOptions options, Rect result) {
- result.setEmpty();
-
- // We start at the last registered {@link LaunchingBoundsPositioner} as this represents
- // The positioner closest to the product level. Moving back through the list moves closer to
- // the platform logic.
- for (int i = mPositioners.size() - 1; i >= 0; --i) {
- mTmpResult.setEmpty();
- mTmpCurrent.set(result);
- final LaunchingBoundsPositioner positioner = mPositioners.get(i);
-
- switch(positioner.onCalculateBounds(task, layout, activity, source, options,
- mTmpCurrent, mTmpResult)) {
- case RESULT_SKIP:
- // Do not apply any results when we are told to skip
- continue;
- case RESULT_DONE:
- // Set result and return immediately.
- result.set(mTmpResult);
- return;
- case RESULT_CONTINUE:
- // Set result and continue
- result.set(mTmpResult);
- break;
- }
- }
- }
-
- /**
- * A convenience method for laying out a task.
- * @return {@code true} if bounds were set on the task. {@code false} otherwise.
- */
- boolean layoutTask(TaskRecord task, WindowLayout layout) {
- return layoutTask(task, layout, null /*activity*/, null /*source*/, null /*options*/);
- }
-
- boolean layoutTask(TaskRecord task, WindowLayout layout, ActivityRecord activity,
- ActivityRecord source, ActivityOptions options) {
- calculateBounds(task, layout, activity, source, options, mTmpRect);
-
- if (mTmpRect.isEmpty()) {
- return false;
- }
-
- task.updateOverrideConfiguration(mTmpRect);
-
- return true;
- }
-
- /**
- * Adds a positioner to participate in future bounds calculation. Note that the last registered
- * {@link LaunchingBoundsPositioner} will be the first to calculate the bounds.
- */
- void registerPositioner(LaunchingBoundsPositioner positioner) {
- if (mPositioners.contains(positioner)) {
- return;
- }
-
- mPositioners.add(positioner);
- }
-
- /**
- * An interface implemented by those wanting to participate in bounds calculation.
- */
- interface LaunchingBoundsPositioner {
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({RESULT_SKIP, RESULT_DONE, RESULT_CONTINUE})
- @interface Result {}
-
- // Returned when the positioner does not want to influence the bounds calculation
- int RESULT_SKIP = 0;
- // Returned when the positioner has changed the bounds and would like its results to be the
- // final bounds applied.
- int RESULT_DONE = 1;
- // Returned when the positioner has changed the bounds but is okay with other positioners
- // influencing the bounds.
- int RESULT_CONTINUE = 2;
-
- /**
- * Called when asked to calculate bounds.
- * @param task The {@link TaskRecord} currently being positioned.
- * @param layout The specified {@link WindowLayout}.
- * @param activity The {@link ActivityRecord} currently being positioned.
- * @param source The {@link ActivityRecord} activity was started from.
- * @param options The {@link ActivityOptions} specified for the activity.
- * @param current The current bounds. This can differ from the initial bounds as it
- * represents the modified bounds up to this point.
- * @param result The {@link Rect} which the positioner should return its modified bounds.
- * Any merging of the current bounds should be already applied to this
- * value as well before returning.
- * @return A {@link Result} representing the result of the bounds calculation.
- */
- @Result
- int onCalculateBounds(TaskRecord task, WindowLayout layout, ActivityRecord activity,
- ActivityRecord source, ActivityOptions options, Rect current, Rect result);
- }
-}
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index ba3e25a..21f9135 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -752,7 +752,7 @@
USER_CURRENT) != 0;
if (shouldLockKeyguard) {
mWindowManager.lockNow(null);
- mWindowManager.dismissKeyguard(null /* callback */);
+ mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
getLockPatternUtils().requireCredentialEntry(USER_ALL);
}
} catch (Settings.SettingNotFoundException e) {
diff --git a/services/core/java/com/android/server/am/LaunchingTaskPositioner.java b/services/core/java/com/android/server/am/TaskLaunchParamsModifier.java
similarity index 93%
rename from services/core/java/com/android/server/am/LaunchingTaskPositioner.java
rename to services/core/java/com/android/server/am/TaskLaunchParamsModifier.java
index d89568e..92f1cc3 100644
--- a/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
+++ b/services/core/java/com/android/server/am/TaskLaunchParamsModifier.java
@@ -21,26 +21,27 @@
import android.app.ActivityOptions;
import android.content.pm.ActivityInfo;
-import android.graphics.Point;
import android.graphics.Rect;
import android.util.Slog;
import android.view.Gravity;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.am.LaunchParamsController.LaunchParams;
+import com.android.server.am.LaunchParamsController.LaunchParamsModifier;
import java.util.ArrayList;
/**
* Determines where a launching task should be positioned and sized on the display.
*
- * The positioner is fairly simple. For the new task it tries default position based on the gravity
+ * The modifier is fairly simple. For the new task it tries default position based on the gravity
* and compares corners of the task with corners of existing tasks. If some two pairs of corners are
* sufficiently close enough, it shifts the bounds of the new task and tries again. When it exhausts
* all possible shifts, it gives up and puts the task in the original position.
*
* Note that the only gravities of concern are the corners and the center.
*/
-class LaunchingTaskPositioner implements LaunchingBoundsController.LaunchingBoundsPositioner {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "LaunchingTaskPositioner" : TAG_AM;
+class TaskLaunchParamsModifier implements LaunchParamsModifier {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskLaunchParamsModifier" : TAG_AM;
// Determines how close window frames/corners have to be to call them colliding.
private static final int BOUNDS_CONFLICT_MIN_DISTANCE = 4;
@@ -70,17 +71,15 @@
private final Rect mTmpProposal = new Rect();
private final Rect mTmpOriginal = new Rect();
- private final Point mDisplaySize = new Point();
-
/**
* Tries to set task's bound in a way that it won't collide with any other task. By colliding
* we mean that two tasks have left-top corner very close to each other, so one might get
* obfuscated by the other one.
*/
@Override
- public int onCalculateBounds(TaskRecord task, ActivityInfo.WindowLayout layout,
- ActivityRecord activity, ActivityRecord source,
- ActivityOptions options, Rect current, Rect result) {
+ public int onCalculate(TaskRecord task, ActivityInfo.WindowLayout layout,
+ ActivityRecord activity, ActivityRecord source, ActivityOptions options,
+ LaunchParams currentParams, LaunchParams outParams) {
// We can only apply positioning if we're in a freeform stack.
if (task == null || task.getStack() == null || !task.inFreeformWindowingMode()) {
return RESULT_SKIP;
@@ -90,9 +89,11 @@
mAvailableRect.set(task.getParent().getBounds());
+ final Rect resultBounds = outParams.mBounds;
+
if (layout == null) {
positionCenter(tasks, mAvailableRect, getFreeformWidth(mAvailableRect),
- getFreeformHeight(mAvailableRect), result);
+ getFreeformHeight(mAvailableRect), resultBounds);
return RESULT_CONTINUE;
}
@@ -102,22 +103,22 @@
int horizontalGravity = layout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
if (verticalGravity == Gravity.TOP) {
if (horizontalGravity == Gravity.RIGHT) {
- positionTopRight(tasks, mAvailableRect, width, height, result);
+ positionTopRight(tasks, mAvailableRect, width, height, resultBounds);
} else {
- positionTopLeft(tasks, mAvailableRect, width, height, result);
+ positionTopLeft(tasks, mAvailableRect, width, height, resultBounds);
}
} else if (verticalGravity == Gravity.BOTTOM) {
if (horizontalGravity == Gravity.RIGHT) {
- positionBottomRight(tasks, mAvailableRect, width, height, result);
+ positionBottomRight(tasks, mAvailableRect, width, height, resultBounds);
} else {
- positionBottomLeft(tasks, mAvailableRect, width, height, result);
+ positionBottomLeft(tasks, mAvailableRect, width, height, resultBounds);
}
} else {
// Some fancy gravity setting that we don't support yet. We just put the activity in the
// center.
Slog.w(TAG, "Received unsupported gravity: " + layout.gravity
+ ", positioning in the center instead.");
- positionCenter(tasks, mAvailableRect, width, height, result);
+ positionCenter(tasks, mAvailableRect, width, height, resultBounds);
}
return RESULT_CONTINUE;
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 4aef95d..b131e86 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -714,7 +714,7 @@
} else if (toStackWindowingMode == WINDOWING_MODE_FREEFORM) {
Rect bounds = getLaunchBounds();
if (bounds == null) {
- mService.mStackSupervisor.getLaunchingBoundsController().layoutTask(this, null);
+ mService.mStackSupervisor.getLaunchParamsController().layoutTask(this, null);
bounds = configBounds;
}
kept = resize(bounds, RESIZE_MODE_FORCED, !mightReplaceWindow, deferResume);
@@ -1838,7 +1838,7 @@
if (mLastNonFullscreenBounds != null) {
updateOverrideConfiguration(mLastNonFullscreenBounds);
} else {
- mService.mStackSupervisor.getLaunchingBoundsController().layoutTask(this, null);
+ mService.mStackSupervisor.getLaunchParamsController().layoutTask(this, null);
}
} else {
updateOverrideConfiguration(inStack.getOverrideBounds());
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index c4fdffa..c7210a8 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -101,6 +101,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -216,6 +217,18 @@
private volatile ArraySet<String> mCurWaitingUserSwitchCallbacks;
/**
+ * Messages for for switching from {@link android.os.UserHandle#SYSTEM}.
+ */
+ @GuardedBy("mLock")
+ private String mSwitchingFromSystemUserMessage;
+
+ /**
+ * Messages for for switching to {@link android.os.UserHandle#SYSTEM}.
+ */
+ @GuardedBy("mLock")
+ private String mSwitchingToSystemUserMessage;
+
+ /**
* Callbacks that are still active after {@link #USER_SWITCH_TIMEOUT_MS}
*/
@GuardedBy("mLock")
@@ -249,39 +262,51 @@
}
}
- void stopRunningUsersLU(int maxRunningUsers) {
- int currentlyRunning = mUserLru.size();
- int i = 0;
- while (currentlyRunning > maxRunningUsers && i < mUserLru.size()) {
- Integer oldUserId = mUserLru.get(i);
- UserState oldUss = mStartedUsers.get(oldUserId);
- if (oldUss == null) {
+ List<Integer> getRunningUsersLU() {
+ ArrayList<Integer> runningUsers = new ArrayList<>();
+ for (Integer userId : mUserLru) {
+ UserState uss = mStartedUsers.get(userId);
+ if (uss == null) {
// Shouldn't happen, but be sane if it does.
- mUserLru.remove(i);
- currentlyRunning--;
continue;
}
- if (oldUss.state == UserState.STATE_STOPPING
- || oldUss.state == UserState.STATE_SHUTDOWN) {
+ if (uss.state == UserState.STATE_STOPPING
+ || uss.state == UserState.STATE_SHUTDOWN) {
// This user is already stopping, doesn't count.
- currentlyRunning--;
- i++;
continue;
}
- if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId) {
- // Owner/System user and current user can't be stopped. We count it as running
- // when it is not a pure system user.
- if (UserInfo.isSystemOnly(oldUserId)) {
- currentlyRunning--;
+ if (userId == UserHandle.USER_SYSTEM) {
+ // We only count system user as running when it is not a pure system user.
+ if (UserInfo.isSystemOnly(userId)) {
+ continue;
}
- i++;
+ }
+ runningUsers.add(userId);
+ }
+ return runningUsers;
+ }
+
+ void stopRunningUsersLU(int maxRunningUsers) {
+ List<Integer> currentlyRunning = getRunningUsersLU();
+ Iterator<Integer> iterator = currentlyRunning.iterator();
+ while (currentlyRunning.size() > maxRunningUsers && iterator.hasNext()) {
+ Integer userId = iterator.next();
+ if (userId == UserHandle.USER_SYSTEM || userId == mCurrentUserId) {
+ // Owner/System user and current user can't be stopped
continue;
}
- // This is a user to be stopped.
- if (stopUsersLU(oldUserId, false, null) == USER_OP_SUCCESS) {
- currentlyRunning--;
+ if (stopUsersLU(userId, false, null) == USER_OP_SUCCESS) {
+ iterator.remove();
}
- i++;
+ }
+ }
+
+ /**
+ * Returns if more users can be started without stopping currently running users.
+ */
+ boolean canStartMoreUsers() {
+ synchronized (mLock) {
+ return getRunningUsersLU().size() < mMaxRunningUsers;
}
}
@@ -768,34 +793,23 @@
/**
* Stops the guest or ephemeral user if it has gone to the background.
*/
- private void stopGuestOrEphemeralUserIfBackground() {
- IntArray userIds = new IntArray();
- synchronized (mLock) {
- final int num = mUserLru.size();
- for (int i = 0; i < num; i++) {
- Integer oldUserId = mUserLru.get(i);
- UserState oldUss = mStartedUsers.get(oldUserId);
- if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId
- || oldUss.state == UserState.STATE_STOPPING
- || oldUss.state == UserState.STATE_SHUTDOWN) {
- continue;
- }
- userIds.add(oldUserId);
- }
+ private void stopGuestOrEphemeralUserIfBackground(int oldUserId) {
+ if (DEBUG_MU) Slog.i(TAG, "Stop guest or ephemeral user if background: " + oldUserId);
+ UserState oldUss = mStartedUsers.get(oldUserId);
+ if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId
+ || oldUss.state == UserState.STATE_STOPPING
+ || oldUss.state == UserState.STATE_SHUTDOWN) {
+ return;
}
- final int userIdsSize = userIds.size();
- for (int i = 0; i < userIdsSize; i++) {
- int oldUserId = userIds.get(i);
- UserInfo userInfo = getUserInfo(oldUserId);
- if (userInfo.isEphemeral()) {
- LocalServices.getService(UserManagerInternal.class).onEphemeralUserStop(oldUserId);
- }
- if (userInfo.isGuest() || userInfo.isEphemeral()) {
- // This is a user to be stopped.
- synchronized (mLock) {
- stopUsersLU(oldUserId, true, null);
- }
- break;
+
+ UserInfo userInfo = getUserInfo(oldUserId);
+ if (userInfo.isEphemeral()) {
+ LocalServices.getService(UserManagerInternal.class).onEphemeralUserStop(oldUserId);
+ }
+ if (userInfo.isGuest() || userInfo.isEphemeral()) {
+ // This is a user to be stopped.
+ synchronized (mLock) {
+ stopUsersLU(oldUserId, true, null);
}
}
}
@@ -1187,7 +1201,8 @@
private void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) {
// The dialog will show and then initiate the user switch by calling startUserInForeground
- mInjector.showUserSwitchingDialog(fromToUserPair.first, fromToUserPair.second);
+ mInjector.showUserSwitchingDialog(fromToUserPair.first, fromToUserPair.second,
+ getSwitchingFromSystemUserMessage(), getSwitchingToSystemUserMessage());
}
private void dispatchForegroundProfileChanged(int userId) {
@@ -1333,7 +1348,7 @@
mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_COMPLETE_MSG,
newUserId, 0));
- stopGuestOrEphemeralUserIfBackground();
+ stopGuestOrEphemeralUserIfBackground(oldUserId);
stopBackgroundUsersIfEnforced(oldUserId);
}
@@ -1797,6 +1812,30 @@
return mLockPatternUtils.isLockScreenDisabled(userId);
}
+ void setSwitchingFromSystemUserMessage(String switchingFromSystemUserMessage) {
+ synchronized (mLock) {
+ mSwitchingFromSystemUserMessage = switchingFromSystemUserMessage;
+ }
+ }
+
+ void setSwitchingToSystemUserMessage(String switchingToSystemUserMessage) {
+ synchronized (mLock) {
+ mSwitchingToSystemUserMessage = switchingToSystemUserMessage;
+ }
+ }
+
+ private String getSwitchingFromSystemUserMessage() {
+ synchronized (mLock) {
+ return mSwitchingFromSystemUserMessage;
+ }
+ }
+
+ private String getSwitchingToSystemUserMessage() {
+ synchronized (mLock) {
+ return mSwitchingToSystemUserMessage;
+ }
+ }
+
void dump(PrintWriter pw, boolean dumpAll) {
synchronized (mLock) {
pw.println(" mStartedUsers:");
@@ -2077,9 +2116,11 @@
mService.installEncryptionUnawareProviders(userId);
}
- void showUserSwitchingDialog(UserInfo fromUser, UserInfo toUser) {
+ void showUserSwitchingDialog(UserInfo fromUser, UserInfo toUser,
+ String switchingFromSystemUserMessage, String switchingToSystemUserMessage) {
Dialog d = new UserSwitchingDialog(mService, mService.mContext, fromUser, toUser,
- true /* above system */);
+ true /* above system */, switchingFromSystemUserMessage,
+ switchingToSystemUserMessage);
d.show();
}
diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java
index 3e6934f..afcba3b 100644
--- a/services/core/java/com/android/server/am/UserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java
@@ -53,7 +53,8 @@
private boolean mStartedUser;
public UserSwitchingDialog(ActivityManagerService service, Context context, UserInfo oldUser,
- UserInfo newUser, boolean aboveSystem) {
+ UserInfo newUser, boolean aboveSystem, String switchingFromSystemUserMessage,
+ String switchingToSystemUserMessage) {
super(context);
mService = service;
@@ -65,7 +66,7 @@
// Custom view due to alignment and font size requirements
View view = LayoutInflater.from(getContext()).inflate(R.layout.user_switching_dialog, null);
- String viewMessage;
+ String viewMessage = null;
if (UserManager.isSplitSystemUser() && newUser.id == UserHandle.USER_SYSTEM) {
viewMessage = res.getString(R.string.user_logging_out_message, oldUser.name);
} else if (UserManager.isDeviceInDemoMode(context)) {
@@ -75,7 +76,17 @@
viewMessage = res.getString(R.string.demo_starting_message);
}
} else {
- viewMessage = res.getString(R.string.user_switching_message, newUser.name);
+ if (oldUser.id == UserHandle.USER_SYSTEM) {
+ viewMessage = switchingFromSystemUserMessage;
+ } else if (newUser.id == UserHandle.USER_SYSTEM) {
+ viewMessage = switchingToSystemUserMessage;
+ }
+
+ // If switchingFromSystemUserMessage or switchingToSystemUserMessage is null, fallback
+ // to system message.
+ if (viewMessage == null) {
+ viewMessage = res.getString(R.string.user_switching_message, newUser.name);
+ }
}
((TextView) view.findViewById(R.id.message)).setText(viewMessage);
setView(view);
diff --git a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
index 7d3b670..10e6cad 100644
--- a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
@@ -25,6 +25,7 @@
import android.hardware.radio.ITunerCallback;
import android.hardware.radio.RadioManager;
import android.os.ParcelableException;
+import android.os.RemoteException;
import android.util.Slog;
import com.android.server.SystemService;
@@ -86,7 +87,7 @@
@Override
public ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig,
- boolean withAudio, ITunerCallback callback) {
+ boolean withAudio, ITunerCallback callback) throws RemoteException {
Slog.i(TAG, "openTuner(" + moduleId + ", _, " + withAudio + ", _)");
enforcePolicyAccess();
if (callback == null) {
diff --git a/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java b/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
index e5090ed..f9b35f5 100644
--- a/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
@@ -21,6 +21,7 @@
import android.graphics.BitmapFactory;
import android.hardware.radio.ITuner;
import android.hardware.radio.ITunerCallback;
+import android.hardware.radio.ProgramList;
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
import android.os.IBinder;
@@ -249,8 +250,7 @@
}
}
- @Override
- public List<RadioManager.ProgramInfo> getProgramList(Map vendorFilter) {
+ List<RadioManager.ProgramInfo> getProgramList(Map vendorFilter) {
Map<String, String> sFilter = vendorFilter;
synchronized (mLock) {
checkNotClosedLocked();
@@ -263,6 +263,16 @@
}
@Override
+ public void startProgramListUpdates(ProgramList.Filter filter) {
+ mTunerCallback.startProgramListUpdates(filter);
+ }
+
+ @Override
+ public void stopProgramListUpdates() {
+ mTunerCallback.stopProgramListUpdates();
+ }
+
+ @Override
public boolean isConfigFlagSupported(int flag) {
return flag == RadioManager.CONFIG_FORCE_ANALOG;
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java b/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
index 673ff88..18f56ed 100644
--- a/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.hardware.radio.ITuner;
import android.hardware.radio.ITunerCallback;
+import android.hardware.radio.ProgramList;
import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioMetadata;
import android.hardware.radio.RadioTuner;
@@ -28,6 +29,10 @@
import java.util.List;
import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
class TunerCallback implements ITunerCallback {
private static final String TAG = "BroadcastRadioService.TunerCallback";
@@ -40,6 +45,8 @@
@NonNull private final Tuner mTuner;
@NonNull private final ITunerCallback mClientCallback;
+ private final AtomicReference<ProgramList.Filter> mProgramListFilter = new AtomicReference<>();
+
TunerCallback(@NonNull Tuner tuner, @NonNull ITunerCallback clientCallback, int halRev) {
mTuner = tuner;
mClientCallback = clientCallback;
@@ -78,6 +85,15 @@
mTuner.close();
}
+ void startProgramListUpdates(@NonNull ProgramList.Filter filter) {
+ mProgramListFilter.set(Objects.requireNonNull(filter));
+ sendProgramListUpdate();
+ }
+
+ void stopProgramListUpdates() {
+ mProgramListFilter.set(null);
+ }
+
@Override
public void onError(int status) {
dispatch(() -> mClientCallback.onError(status));
@@ -121,6 +137,28 @@
@Override
public void onProgramListChanged() {
dispatch(() -> mClientCallback.onProgramListChanged());
+ sendProgramListUpdate();
+ }
+
+ private void sendProgramListUpdate() {
+ ProgramList.Filter filter = mProgramListFilter.get();
+ if (filter == null) return;
+
+ List<RadioManager.ProgramInfo> modified;
+ try {
+ modified = mTuner.getProgramList(filter.getVendorFilter());
+ } catch (IllegalStateException ex) {
+ Slog.d(TAG, "Program list not ready yet");
+ return;
+ }
+ Set<RadioManager.ProgramInfo> modifiedSet = modified.stream().collect(Collectors.toSet());
+ ProgramList.Chunk chunk = new ProgramList.Chunk(true, true, modifiedSet, null);
+ dispatch(() -> mClientCallback.onProgramListUpdated(chunk));
+ }
+
+ @Override
+ public void onProgramListUpdated(ProgramList.Chunk chunk) {
+ dispatch(() -> mClientCallback.onProgramListUpdated(chunk));
}
@Override
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
index 9158ff0..fc9a5d6 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -82,7 +82,7 @@
}
public ITuner openSession(int moduleId, @Nullable RadioManager.BandConfig legacyConfig,
- boolean withAudio, @NonNull ITunerCallback callback) {
+ boolean withAudio, @NonNull ITunerCallback callback) throws RemoteException {
Objects.requireNonNull(callback);
if (!withAudio) {
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
index 2c129bb..60a927c 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
@@ -20,15 +20,22 @@
import android.annotation.Nullable;
import android.hardware.broadcastradio.V2_0.AmFmBandRange;
import android.hardware.broadcastradio.V2_0.AmFmRegionConfig;
+import android.hardware.broadcastradio.V2_0.ProgramFilter;
+import android.hardware.broadcastradio.V2_0.ProgramIdentifier;
+import android.hardware.broadcastradio.V2_0.ProgramInfo;
+import android.hardware.broadcastradio.V2_0.ProgramInfoFlags;
+import android.hardware.broadcastradio.V2_0.ProgramListChunk;
import android.hardware.broadcastradio.V2_0.Properties;
import android.hardware.broadcastradio.V2_0.Result;
import android.hardware.broadcastradio.V2_0.VendorKeyValue;
+import android.hardware.radio.ProgramList;
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
import android.os.ParcelableException;
import android.util.Slog;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -36,6 +43,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
class Convert {
private static final String TAG = "BcRadio2Srv.convert";
@@ -78,43 +86,52 @@
return map;
}
+ private static @ProgramSelector.ProgramType int identifierTypeToProgramType(
+ @ProgramSelector.IdentifierType int idType) {
+ switch (idType) {
+ case ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY:
+ case ProgramSelector.IDENTIFIER_TYPE_RDS_PI:
+ // TODO(b/69958423): verify AM/FM with frequency range
+ return ProgramSelector.PROGRAM_TYPE_FM;
+ case ProgramSelector.IDENTIFIER_TYPE_HD_STATION_ID_EXT:
+ // TODO(b/69958423): verify AM/FM with frequency range
+ return ProgramSelector.PROGRAM_TYPE_FM_HD;
+ case ProgramSelector.IDENTIFIER_TYPE_DAB_SIDECC:
+ case ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE:
+ case ProgramSelector.IDENTIFIER_TYPE_DAB_SCID:
+ case ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY:
+ return ProgramSelector.PROGRAM_TYPE_DAB;
+ case ProgramSelector.IDENTIFIER_TYPE_DRMO_SERVICE_ID:
+ case ProgramSelector.IDENTIFIER_TYPE_DRMO_FREQUENCY:
+ return ProgramSelector.PROGRAM_TYPE_DRMO;
+ case ProgramSelector.IDENTIFIER_TYPE_SXM_SERVICE_ID:
+ case ProgramSelector.IDENTIFIER_TYPE_SXM_CHANNEL:
+ return ProgramSelector.PROGRAM_TYPE_SXM;
+ }
+ if (idType >= ProgramSelector.IDENTIFIER_TYPE_VENDOR_PRIMARY_START
+ && idType <= ProgramSelector.IDENTIFIER_TYPE_VENDOR_PRIMARY_END) {
+ return idType;
+ }
+ return ProgramSelector.PROGRAM_TYPE_INVALID;
+ }
+
private static @NonNull int[]
identifierTypesToProgramTypes(@NonNull int[] idTypes) {
Set<Integer> pTypes = new HashSet<>();
for (int idType : idTypes) {
- switch (idType) {
- case ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY:
- case ProgramSelector.IDENTIFIER_TYPE_RDS_PI:
- // TODO(b/69958423): verify AM/FM with region info
- pTypes.add(ProgramSelector.PROGRAM_TYPE_AM);
- pTypes.add(ProgramSelector.PROGRAM_TYPE_FM);
- break;
- case ProgramSelector.IDENTIFIER_TYPE_HD_STATION_ID_EXT:
- // TODO(b/69958423): verify AM/FM with region info
- pTypes.add(ProgramSelector.PROGRAM_TYPE_AM_HD);
- pTypes.add(ProgramSelector.PROGRAM_TYPE_FM_HD);
- break;
- case ProgramSelector.IDENTIFIER_TYPE_DAB_SIDECC:
- case ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE:
- case ProgramSelector.IDENTIFIER_TYPE_DAB_SCID:
- case ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY:
- pTypes.add(ProgramSelector.PROGRAM_TYPE_DAB);
- break;
- case ProgramSelector.IDENTIFIER_TYPE_DRMO_SERVICE_ID:
- case ProgramSelector.IDENTIFIER_TYPE_DRMO_FREQUENCY:
- pTypes.add(ProgramSelector.PROGRAM_TYPE_DRMO);
- break;
- case ProgramSelector.IDENTIFIER_TYPE_SXM_SERVICE_ID:
- case ProgramSelector.IDENTIFIER_TYPE_SXM_CHANNEL:
- pTypes.add(ProgramSelector.PROGRAM_TYPE_SXM);
- break;
- default:
- break;
+ int pType = identifierTypeToProgramType(idType);
+
+ if (pType == ProgramSelector.PROGRAM_TYPE_INVALID) continue;
+
+ pTypes.add(pType);
+ if (pType == ProgramSelector.PROGRAM_TYPE_FM) {
+ // TODO(b/69958423): verify AM/FM with region info
+ pTypes.add(ProgramSelector.PROGRAM_TYPE_AM);
}
- if (idType >= ProgramSelector.IDENTIFIER_TYPE_VENDOR_PRIMARY_START
- && idType <= ProgramSelector.IDENTIFIER_TYPE_VENDOR_PRIMARY_END) {
- pTypes.add(idType);
+ if (pType == ProgramSelector.PROGRAM_TYPE_FM_HD) {
+ // TODO(b/69958423): verify AM/FM with region info
+ pTypes.add(ProgramSelector.PROGRAM_TYPE_AM_HD);
}
}
@@ -189,6 +206,64 @@
false, // isBgScanSupported is deprecated
supportedProgramTypes,
supportedIdentifierTypes,
- vendorInfoFromHal(prop.vendorInfo));
+ vendorInfoFromHal(prop.vendorInfo)
+ );
+ }
+
+ static @NonNull ProgramIdentifier programIdentifierToHal(
+ @NonNull ProgramSelector.Identifier id) {
+ ProgramIdentifier hwId = new ProgramIdentifier();
+ hwId.type = id.getType();
+ hwId.value = id.getValue();
+ return hwId;
+ }
+
+ static @NonNull ProgramSelector.Identifier programIdentifierFromHal(@NonNull ProgramIdentifier id) {
+ return new ProgramSelector.Identifier(id.type, id.value);
+ }
+
+ static @NonNull ProgramSelector programSelectorFromHal(
+ @NonNull android.hardware.broadcastradio.V2_0.ProgramSelector sel) {
+ ProgramSelector.Identifier[] secondaryIds = sel.secondaryIds.stream().map(
+ id -> programIdentifierFromHal(id)).toArray(ProgramSelector.Identifier[]::new);
+
+ return new ProgramSelector(
+ identifierTypeToProgramType(sel.primaryId.type),
+ programIdentifierFromHal(sel.primaryId),
+ secondaryIds, null);
+ }
+
+ static @NonNull RadioManager.ProgramInfo programInfoFromHal(@NonNull ProgramInfo info) {
+ return new RadioManager.ProgramInfo(
+ programSelectorFromHal(info.selector),
+ (info.infoFlags & ProgramInfoFlags.TUNED) != 0,
+ (info.infoFlags & ProgramInfoFlags.STEREO) != 0,
+ false, // TODO(b/69860743): digital
+ info.signalQuality,
+ null, // TODO(b/69860743): metadata
+ info.infoFlags,
+ vendorInfoFromHal(info.vendorInfo)
+ );
+ }
+
+ static @NonNull ProgramFilter programFilterToHal(@NonNull ProgramList.Filter filter) {
+ ProgramFilter hwFilter = new ProgramFilter();
+
+ filter.getIdentifierTypes().stream().forEachOrdered(hwFilter.identifierTypes::add);
+ filter.getIdentifiers().stream().forEachOrdered(
+ id -> hwFilter.identifiers.add(programIdentifierToHal(id)));
+ hwFilter.includeCategories = filter.areCategoriesIncluded();
+ hwFilter.excludeModifications = filter.areModificationsExcluded();
+
+ return hwFilter;
+ }
+
+ static @NonNull ProgramList.Chunk programListChunkFromHal(@NonNull ProgramListChunk chunk) {
+ Set<RadioManager.ProgramInfo> modified = chunk.modified.stream().map(
+ info -> programInfoFromHal(info)).collect(Collectors.toSet());
+ Set<ProgramSelector.Identifier> removed = chunk.removed.stream().map(
+ id -> programIdentifierFromHal(id)).collect(Collectors.toSet());
+
+ return new ProgramList.Chunk(chunk.purge, chunk.complete, modified, removed);
}
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index 45b2190..c8e15c1 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -63,20 +63,16 @@
}
}
- public @NonNull TunerSession openSession(@NonNull android.hardware.radio.ITunerCallback userCb) {
+ public @NonNull TunerSession openSession(@NonNull android.hardware.radio.ITunerCallback userCb)
+ throws RemoteException {
TunerCallback cb = new TunerCallback(Objects.requireNonNull(userCb));
Mutable<ITunerSession> hwSession = new Mutable<>();
MutableInt halResult = new MutableInt(Result.UNKNOWN_ERROR);
- try {
- mService.openSession(cb, (int result, ITunerSession session) -> {
- hwSession.value = session;
- halResult.value = result;
- });
- } catch (RemoteException ex) {
- Slog.e(TAG, "failed to open session", ex);
- throw new ParcelableException(ex);
- }
+ mService.openSession(cb, (int result, ITunerSession session) -> {
+ hwSession.value = session;
+ halResult.value = result;
+ });
Convert.throwOnError("openSession", halResult.value);
Objects.requireNonNull(hwSession.value);
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerCallback.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerCallback.java
index c9084ee..ed2a1b3 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerCallback.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerCallback.java
@@ -56,7 +56,9 @@
public void onCurrentProgramInfoChanged(ProgramInfo info) {}
@Override
- public void onProgramListUpdated(ProgramListChunk chunk) {}
+ public void onProgramListUpdated(ProgramListChunk chunk) {
+ dispatch(() -> mClientCb.onProgramListUpdated(Convert.programListChunkFromHal(chunk)));
+ }
@Override
public void onAntennaStateChange(boolean connected) {}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index 8ed646a..e093c9d 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -22,6 +22,7 @@
import android.hardware.broadcastradio.V2_0.ITunerSession;
import android.hardware.broadcastradio.V2_0.Result;
import android.hardware.radio.ITuner;
+import android.hardware.radio.ProgramList;
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
import android.media.AudioSystem;
@@ -184,10 +185,19 @@
}
@Override
- public List<RadioManager.ProgramInfo> getProgramList(Map vendorFilter) {
+ public void startProgramListUpdates(ProgramList.Filter filter) throws RemoteException {
synchronized (mLock) {
checkNotClosedLocked();
- return null;
+ int halResult = mHwSession.startProgramListUpdates(Convert.programFilterToHal(filter));
+ Convert.throwOnError("startProgramListUpdates", halResult);
+ }
+ }
+
+ @Override
+ public void stopProgramListUpdates() throws RemoteException {
+ synchronized (mLock) {
+ checkNotClosedLocked();
+ mHwSession.stopProgramListUpdates();
}
}
@@ -226,17 +236,12 @@
}
@Override
- public void setConfigFlag(int flag, boolean value) {
+ public void setConfigFlag(int flag, boolean value) throws RemoteException {
Slog.v(TAG, "setConfigFlag " + ConfigFlag.toString(flag) + " = " + value);
synchronized (mLock) {
checkNotClosedLocked();
- int halResult;
- try {
- halResult = mHwSession.setConfigFlag(flag, value);
- } catch (RemoteException ex) {
- throw new RuntimeException("Failed to set flag " + ConfigFlag.toString(flag), ex);
- }
+ int halResult = mHwSession.setConfigFlag(flag, value);
Convert.throwOnError("setConfigFlag", halResult);
}
}
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
new file mode 100644
index 0000000..a1c54bd
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+import static android.provider.Settings.Global.DNS_RESOLVER_MIN_SAMPLES;
+import static android.provider.Settings.Global.DNS_RESOLVER_MAX_SAMPLES;
+import static android.provider.Settings.Global.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS;
+import static android.provider.Settings.Global.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT;
+import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
+import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkUtils;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.INetworkManagementService;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.system.GaiException;
+import android.system.OsConstants;
+import android.system.StructAddrinfo;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.server.connectivity.MockableSystemProperties;
+
+import libcore.io.Libcore;
+
+import java.net.InetAddress;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.StringJoiner;
+
+
+/**
+ * Encapsulate the management of DNS settings for networks.
+ *
+ * This class it NOT designed for concurrent access. Furthermore, all non-static
+ * methods MUST be called from ConnectivityService's thread.
+ *
+ * @hide
+ */
+public class DnsManager {
+ private static final String TAG = DnsManager.class.getSimpleName();
+
+ /* Defaults for resolver parameters. */
+ private static final int DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800;
+ private static final int DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT = 25;
+ private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
+ private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
+
+ public static class PrivateDnsConfig {
+ public final boolean useTls;
+ public final String hostname;
+ public final InetAddress[] ips;
+
+ public PrivateDnsConfig() {
+ this(false);
+ }
+
+ public PrivateDnsConfig(boolean useTls) {
+ this.useTls = useTls;
+ this.hostname = "";
+ this.ips = new InetAddress[0];
+ }
+
+ public PrivateDnsConfig(String hostname, InetAddress[] ips) {
+ this.useTls = !TextUtils.isEmpty(hostname);
+ this.hostname = useTls ? hostname : "";
+ this.ips = (ips != null) ? ips : new InetAddress[0];
+ }
+
+ public PrivateDnsConfig(PrivateDnsConfig cfg) {
+ useTls = cfg.useTls;
+ hostname = cfg.hostname;
+ ips = cfg.ips;
+ }
+
+ public boolean inStrictMode() {
+ return useTls && !TextUtils.isEmpty(hostname);
+ }
+
+ public String toString() {
+ return PrivateDnsConfig.class.getSimpleName() +
+ "{" + useTls + ":" + hostname + "/" + Arrays.toString(ips) + "}";
+ }
+ }
+
+ public static PrivateDnsConfig getPrivateDnsConfig(ContentResolver cr) {
+ final String mode = getPrivateDnsMode(cr);
+
+ final boolean useTls = !TextUtils.isEmpty(mode) && !PRIVATE_DNS_MODE_OFF.equals(mode);
+
+ if (PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals(mode)) {
+ final String specifier = getStringSetting(cr, PRIVATE_DNS_SPECIFIER);
+ return new PrivateDnsConfig(specifier, null);
+ }
+
+ return new PrivateDnsConfig(useTls);
+ }
+
+ public static PrivateDnsConfig tryBlockingResolveOf(Network network, String name) {
+ final StructAddrinfo hints = new StructAddrinfo();
+ // Unnecessary, but expressly no AI_ADDRCONFIG.
+ hints.ai_flags = 0;
+ // Fetch all IP addresses at once to minimize re-resolution.
+ hints.ai_family = OsConstants.AF_UNSPEC;
+ hints.ai_socktype = OsConstants.SOCK_DGRAM;
+
+ try {
+ final InetAddress[] ips = Libcore.os.android_getaddrinfo(name, hints, network.netId);
+ if (ips != null && ips.length > 0) {
+ return new PrivateDnsConfig(name, ips);
+ }
+ } catch (GaiException ignored) {}
+
+ return null;
+ }
+
+ public static Uri[] getPrivateDnsSettingsUris() {
+ final Uri[] uris = new Uri[2];
+ uris[0] = Settings.Global.getUriFor(PRIVATE_DNS_MODE);
+ uris[1] = Settings.Global.getUriFor(PRIVATE_DNS_SPECIFIER);
+ return uris;
+ }
+
+ private final Context mContext;
+ private final ContentResolver mContentResolver;
+ private final INetworkManagementService mNMS;
+ private final MockableSystemProperties mSystemProperties;
+ private final Map<Integer, PrivateDnsConfig> mPrivateDnsMap;
+
+ private int mNumDnsEntries;
+ private int mSampleValidity;
+ private int mSuccessThreshold;
+ private int mMinSamples;
+ private int mMaxSamples;
+ private String mPrivateDnsMode;
+ private String mPrivateDnsSpecifier;
+
+ public DnsManager(Context ctx, INetworkManagementService nms, MockableSystemProperties sp) {
+ mContext = ctx;
+ mContentResolver = mContext.getContentResolver();
+ mNMS = nms;
+ mSystemProperties = sp;
+ mPrivateDnsMap = new HashMap<>();
+
+ // TODO: Create and register ContentObservers to track every setting
+ // used herein, posting messages to respond to changes.
+ }
+
+ public PrivateDnsConfig getPrivateDnsConfig() {
+ return getPrivateDnsConfig(mContentResolver);
+ }
+
+ public void removeNetwork(Network network) {
+ mPrivateDnsMap.remove(network.netId);
+ }
+
+ public PrivateDnsConfig updatePrivateDns(Network network, PrivateDnsConfig cfg) {
+ Slog.w(TAG, "updatePrivateDns(" + network + ", " + cfg + ")");
+ return (cfg != null)
+ ? mPrivateDnsMap.put(network.netId, cfg)
+ : mPrivateDnsMap.remove(network);
+ }
+
+ public void setDnsConfigurationForNetwork(
+ int netId, LinkProperties lp, boolean isDefaultNetwork) {
+ // We only use the PrivateDnsConfig data pushed to this class instance
+ // from ConnectivityService because it works in coordination with
+ // NetworkMonitor to decide which networks need validation and runs the
+ // blocking calls to resolve Private DNS strict mode hostnames.
+ //
+ // At this time we do attempt to enable Private DNS on non-Internet
+ // networks like IMS.
+ final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.get(netId);
+
+ final boolean useTls = (privateDnsCfg != null) && privateDnsCfg.useTls;
+ final boolean strictMode = (privateDnsCfg != null) && privateDnsCfg.inStrictMode();
+ final String tlsHostname = strictMode ? privateDnsCfg.hostname : "";
+
+ final String[] serverStrs = NetworkUtils.makeStrings(
+ strictMode ? Arrays.stream(privateDnsCfg.ips)
+ .filter((ip) -> lp.isReachable(ip))
+ .collect(Collectors.toList())
+ : lp.getDnsServers());
+ final String[] domainStrs = getDomainStrings(lp.getDomains());
+
+ updateParametersSettings();
+ final int[] params = { mSampleValidity, mSuccessThreshold, mMinSamples, mMaxSamples };
+
+ Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %s, %s, %s)",
+ netId, Arrays.toString(serverStrs), Arrays.toString(domainStrs),
+ Arrays.toString(params), useTls, tlsHostname));
+ try {
+ mNMS.setDnsConfigurationForNetwork(
+ netId, serverStrs, domainStrs, params, useTls, tlsHostname);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error setting DNS configuration: " + e);
+ return;
+ }
+
+ // TODO: netd should listen on [::1]:53 and proxy queries to the current
+ // default network, and we should just set net.dns1 to ::1, not least
+ // because applications attempting to use net.dns resolvers will bypass
+ // the privacy protections of things like DNS-over-TLS.
+ if (isDefaultNetwork) setDefaultDnsSystemProperties(lp.getDnsServers());
+ flushVmDnsCache();
+ }
+
+ public void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) {
+ int last = 0;
+ for (InetAddress dns : dnses) {
+ ++last;
+ setNetDnsProperty(last, dns.getHostAddress());
+ }
+ for (int i = last + 1; i <= mNumDnsEntries; ++i) {
+ setNetDnsProperty(i, "");
+ }
+ mNumDnsEntries = last;
+ }
+
+ private void flushVmDnsCache() {
+ /*
+ * Tell the VMs to toss their DNS caches
+ */
+ final Intent intent = new Intent(Intent.ACTION_CLEAR_DNS_CACHE);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ /*
+ * Connectivity events can happen before boot has completed ...
+ */
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void updateParametersSettings() {
+ mSampleValidity = getIntSetting(
+ DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS,
+ DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
+ if (mSampleValidity < 0 || mSampleValidity > 65535) {
+ Slog.w(TAG, "Invalid sampleValidity=" + mSampleValidity + ", using default=" +
+ DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
+ mSampleValidity = DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS;
+ }
+
+ mSuccessThreshold = getIntSetting(
+ DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT,
+ DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
+ if (mSuccessThreshold < 0 || mSuccessThreshold > 100) {
+ Slog.w(TAG, "Invalid successThreshold=" + mSuccessThreshold + ", using default=" +
+ DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
+ mSuccessThreshold = DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT;
+ }
+
+ mMinSamples = getIntSetting(DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES);
+ mMaxSamples = getIntSetting(DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES);
+ if (mMinSamples < 0 || mMinSamples > mMaxSamples || mMaxSamples > 64) {
+ Slog.w(TAG, "Invalid sample count (min, max)=(" + mMinSamples + ", " + mMaxSamples +
+ "), using default=(" + DNS_RESOLVER_DEFAULT_MIN_SAMPLES + ", " +
+ DNS_RESOLVER_DEFAULT_MAX_SAMPLES + ")");
+ mMinSamples = DNS_RESOLVER_DEFAULT_MIN_SAMPLES;
+ mMaxSamples = DNS_RESOLVER_DEFAULT_MAX_SAMPLES;
+ }
+ }
+
+ private int getIntSetting(String which, int dflt) {
+ return Settings.Global.getInt(mContentResolver, which, dflt);
+ }
+
+ private void setNetDnsProperty(int which, String value) {
+ final String key = "net.dns" + which;
+ // Log and forget errors setting unsupported properties.
+ try {
+ mSystemProperties.set(key, value);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error setting unsupported net.dns property: ", e);
+ }
+ }
+
+ private static String getPrivateDnsMode(ContentResolver cr) {
+ final String mode = getStringSetting(cr, PRIVATE_DNS_MODE);
+ return !TextUtils.isEmpty(mode) ? mode : PRIVATE_DNS_DEFAULT_MODE;
+ }
+
+ private static String getStringSetting(ContentResolver cr, String which) {
+ return Settings.Global.getString(cr, which);
+ }
+
+ private static String[] getDomainStrings(String domains) {
+ return (TextUtils.isEmpty(domains)) ? new String[0] : domains.split(" ");
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 7684030..ed268581b 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -29,6 +29,7 @@
import android.net.ConnectivityManager;
import android.net.ICaptivePortal;
import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.ProxyInfo;
import android.net.TrafficStats;
@@ -215,6 +216,15 @@
*/
private static final int CMD_CAPTIVE_PORTAL_RECHECK = BASE + 12;
+ /**
+ * ConnectivityService notifies NetworkMonitor of settings changes to
+ * Private DNS. If a DNS resolution is required, e.g. for DNS-over-TLS in
+ * strict mode, then an event is sent back to ConnectivityService with the
+ * result of the resolution attempt.
+ */
+ private static final int CMD_PRIVATE_DNS_SETTINGS_CHANGED = BASE + 13;
+ public static final int EVENT_PRIVATE_DNS_CONFIG_RESOLVED = BASE + 14;
+
// Start mReevaluateDelayMs at this value and double.
private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
private static final int MAX_REEVALUATE_DELAY_MS = 10*60*1000;
@@ -230,6 +240,12 @@
private static final int NUM_VALIDATION_LOG_LINES = 20;
+ public static boolean isValidationRequired(
+ NetworkCapabilities dfltNetCap, NetworkCapabilities nc) {
+ // TODO: Consider requiring validation for DUN networks.
+ return dfltNetCap.satisfiedByNetworkCapabilities(nc);
+ }
+
private final Context mContext;
private final Handler mConnectivityServiceHandler;
private final NetworkAgentInfo mNetworkAgentInfo;
@@ -261,6 +277,8 @@
public boolean systemReady = false;
+ private DnsManager.PrivateDnsConfig mPrivateDnsCfg = null;
+
private final State mDefaultState = new DefaultState();
private final State mValidatedState = new ValidatedState();
private final State mMaybeNotifyState = new MaybeNotifyState();
@@ -342,6 +360,11 @@
return 0 == mValidations ? ValidationStage.FIRST_VALIDATION : ValidationStage.REVALIDATION;
}
+ private boolean isValidationRequired() {
+ return isValidationRequired(
+ mDefaultRequest.networkCapabilities, mNetworkAgentInfo.networkCapabilities);
+ }
+
// DefaultState is the parent of all States. It exists only to handle CMD_* messages but
// does not entail any real state (hence no enter() or exit() routines).
private class DefaultState extends State {
@@ -405,6 +428,18 @@
break;
}
return HANDLED;
+ case CMD_PRIVATE_DNS_SETTINGS_CHANGED:
+ if (isValidationRequired()) {
+ // This performs a blocking DNS resolution of the
+ // strict mode hostname, if required.
+ resolvePrivateDnsConfig((DnsManager.PrivateDnsConfig) message.obj);
+ if ((mPrivateDnsCfg != null) && mPrivateDnsCfg.inStrictMode()) {
+ mConnectivityServiceHandler.sendMessage(obtainMessage(
+ EVENT_PRIVATE_DNS_CONFIG_RESOLVED, 0, mNetId,
+ new DnsManager.PrivateDnsConfig(mPrivateDnsCfg)));
+ }
+ }
+ return HANDLED;
default:
return HANDLED;
}
@@ -421,7 +456,7 @@
maybeLogEvaluationResult(
networkEventType(validationStage(), EvaluationResult.VALIDATED));
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
- NETWORK_TEST_RESULT_VALID, mNetId, null));
+ NETWORK_TEST_RESULT_VALID, mNetId, mPrivateDnsCfg));
mValidations++;
}
@@ -567,9 +602,9 @@
// the network so don't bother validating here. Furthermore sending HTTP
// packets over the network may be undesirable, for example an extremely
// expensive metered network, or unwanted leaking of the User Agent string.
- if (!mDefaultRequest.networkCapabilities.satisfiedByNetworkCapabilities(
- mNetworkAgentInfo.networkCapabilities)) {
+ if (!isValidationRequired()) {
validationLog("Network would not satisfy default request, not validating");
+ mPrivateDnsCfg = null;
transitionTo(mValidatedState);
return HANDLED;
}
@@ -582,6 +617,7 @@
// if this is found to cause problems.
CaptivePortalProbeResult probeResult = isCaptivePortal();
if (probeResult.isSuccessful()) {
+ resolvePrivateDnsConfig();
transitionTo(mValidatedState);
} else if (probeResult.isPortal()) {
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
@@ -1045,6 +1081,44 @@
return null;
}
+ public void notifyPrivateDnsSettingsChanged(DnsManager.PrivateDnsConfig newCfg) {
+ // Cancel any outstanding resolutions.
+ removeMessages(CMD_PRIVATE_DNS_SETTINGS_CHANGED);
+ // Send the update to the proper thread.
+ sendMessage(CMD_PRIVATE_DNS_SETTINGS_CHANGED, newCfg);
+ }
+
+ private void resolvePrivateDnsConfig() {
+ resolvePrivateDnsConfig(DnsManager.getPrivateDnsConfig(mContext.getContentResolver()));
+ }
+
+ private void resolvePrivateDnsConfig(DnsManager.PrivateDnsConfig cfg) {
+ // Nothing to do.
+ if (cfg == null) {
+ mPrivateDnsCfg = null;
+ return;
+ }
+
+ // No DNS resolution required.
+ if (!cfg.inStrictMode()) {
+ mPrivateDnsCfg = cfg;
+ return;
+ }
+
+ if ((mPrivateDnsCfg != null) && mPrivateDnsCfg.inStrictMode() &&
+ (mPrivateDnsCfg.ips.length > 0) && mPrivateDnsCfg.hostname.equals(cfg.hostname)) {
+ // We have already resolved this strict mode hostname. Assume that
+ // Private DNS services won't be changing serving IP addresses very
+ // frequently and save ourselves one re-resolve.
+ return;
+ }
+
+ mPrivateDnsCfg = cfg;
+ final DnsManager.PrivateDnsConfig resolvedCfg = DnsManager.tryBlockingResolveOf(
+ mNetwork, mPrivateDnsCfg.hostname);
+ if (resolvedCfg != null) mPrivateDnsCfg = resolvedCfg;
+ }
+
/**
* @param responseReceived - whether or not we received a valid HTTP response to our request.
* If false, isCaptivePortal and responseTimestampMs are ignored
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index c7a4315..aa174e3 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.BIND_VPN_SERVICE;
import static android.net.ConnectivityManager.NETID_UNSET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.RouteInfo.RTN_THROW;
@@ -128,7 +129,7 @@
// Length of time (in milliseconds) that an app hosting an always-on VPN is placed on
// the device idle whitelist during service launch and VPN bootstrap.
- private static final long VPN_LAUNCH_IDLE_WHITELIST_DURATION = 60 * 1000;
+ private static final long VPN_LAUNCH_IDLE_WHITELIST_DURATION_MS = 60 * 1000;
// TODO: create separate trackers for each unique VPN to support
// automated reconnection
@@ -183,10 +184,10 @@
@GuardedBy("this")
private Set<UidRange> mBlockedUsers = new ArraySet<>();
- // Handle of user initiating VPN.
+ // Handle of the user initiating VPN.
private final int mUserHandle;
- // Listen to package remove and change event in this user
+ // Listen to package removal and change events (update/uninstall) for this user
private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -197,14 +198,14 @@
}
synchronized (Vpn.this) {
- // Avoid race that always-on package has been unset
+ // Avoid race where always-on package has been unset
if (!packageName.equals(getAlwaysOnPackage())) {
return;
}
final String action = intent.getAction();
- Log.i(TAG, "Received broadcast " + action + " for always-on package " + packageName
- + " in user " + mUserHandle);
+ Log.i(TAG, "Received broadcast " + action + " for always-on VPN package "
+ + packageName + " in user " + mUserHandle);
switch(action) {
case Intent.ACTION_PACKAGE_REPLACED:
@@ -248,7 +249,8 @@
Log.wtf(TAG, "Problem registering observer", e);
}
- mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0, NETWORKTYPE, "");
+ mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0 /* subtype */, NETWORKTYPE,
+ "" /* subtypeName */);
mNetworkCapabilities = new NetworkCapabilities();
mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
@@ -258,7 +260,7 @@
}
/**
- * Set if this object is responsible for watching for {@link NetworkInfo}
+ * Set whether this object is responsible for watching for {@link NetworkInfo}
* teardown. When {@code false}, teardown is handled externally by someone
* else.
*/
@@ -297,11 +299,13 @@
int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
boolean metered = false;
boolean roaming = false;
+ boolean congested = false;
if (ArrayUtils.isEmpty(underlyingNetworks)) {
// No idea what the underlying networks are; assume sane defaults
metered = true;
roaming = false;
+ congested = false;
} else {
for (Network underlying : underlyingNetworks) {
final NetworkCapabilities underlyingCaps = cm.getNetworkCapabilities(underlying);
@@ -318,22 +322,16 @@
underlyingCaps.getLinkUpstreamBandwidthKbps());
metered |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_METERED);
roaming |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ congested |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_CONGESTED);
}
}
caps.setTransportTypes(transportTypes);
caps.setLinkDownstreamBandwidthKbps(downKbps);
caps.setLinkUpstreamBandwidthKbps(upKbps);
- if (metered) {
- caps.removeCapability(NET_CAPABILITY_NOT_METERED);
- } else {
- caps.addCapability(NET_CAPABILITY_NOT_METERED);
- }
- if (roaming) {
- caps.removeCapability(NET_CAPABILITY_NOT_ROAMING);
- } else {
- caps.addCapability(NET_CAPABILITY_NOT_ROAMING);
- }
+ caps.setCapability(NET_CAPABILITY_NOT_METERED, !metered);
+ caps.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming);
+ caps.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested);
}
/**
@@ -481,7 +479,6 @@
}
private void unregisterPackageChangeReceiverLocked() {
- // register previous intent filter
if (mIsPackageIntentReceiverRegistered) {
mContext.unregisterReceiver(mPackageIntentReceiver);
mIsPackageIntentReceiverRegistered = false;
@@ -582,7 +579,7 @@
DeviceIdleController.LocalService idleController =
LocalServices.getService(DeviceIdleController.LocalService.class);
idleController.addPowerSaveTempWhitelistApp(Process.myUid(), alwaysOnPackage,
- VPN_LAUNCH_IDLE_WHITELIST_DURATION, mUserHandle, false, "vpn");
+ VPN_LAUNCH_IDLE_WHITELIST_DURATION_MS, mUserHandle, false, "vpn");
// Start the VPN service declared in the app's manifest.
Intent serviceIntent = new Intent(VpnConfig.SERVICE_INTERFACE);
@@ -612,9 +609,10 @@
* It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
* it can be revoked by itself.
*
- * Note: when we added VPN pre-consent in http://ag/522961 the names oldPackage
- * and newPackage become misleading, because when an app is pre-consented, we
- * actually prepare oldPackage, not newPackage.
+ * Note: when we added VPN pre-consent in
+ * https://android.googlesource.com/platform/frameworks/base/+/0554260
+ * the names oldPackage and newPackage became misleading, because when
+ * an app is pre-consented, we actually prepare oldPackage, not newPackage.
*
* Their meanings actually are:
*
@@ -630,7 +628,7 @@
* @param oldPackage The package name of the old VPN application
* @param newPackage The package name of the new VPN application
*
- * @return true if the operation is succeeded.
+ * @return true if the operation succeeded.
*/
public synchronized boolean prepare(String oldPackage, String newPackage) {
if (oldPackage != null) {
@@ -639,7 +637,7 @@
return false;
}
- // Package is not same or old package was reinstalled.
+ // Package is not the same or old package was reinstalled.
if (!isCurrentPreparedPackage(oldPackage)) {
// The package doesn't match. We return false (to obtain user consent) unless the
// user has already consented to that VPN package.
@@ -861,8 +859,8 @@
long token = Binder.clearCallingIdentity();
try {
- mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE,
- mNetworkInfo, mNetworkCapabilities, lp, 0, networkMisc) {
+ mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE /* logtag */,
+ mNetworkInfo, mNetworkCapabilities, lp, 0 /* score */, networkMisc) {
@Override
public void unwanted() {
// We are user controlled, not driven by NetworkRequest.
@@ -936,7 +934,7 @@
}
ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent,
- null, 0, mUserHandle);
+ null, 0, mUserHandle);
if (info == null) {
throw new SecurityException("Cannot find " + config.user);
}
@@ -944,7 +942,7 @@
throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
}
} catch (RemoteException e) {
- throw new SecurityException("Cannot find " + config.user);
+ throw new SecurityException("Cannot find " + config.user);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1337,7 +1335,7 @@
}
private void enforceControlPermissionOrInternalCaller() {
- // Require caller to be either an application with CONTROL_VPN permission or a process
+ // Require the caller to be either an application with CONTROL_VPN permission or a process
// in the system server.
mContext.enforceCallingOrSelfPermission(Manifest.permission.CONTROL_VPN,
"Unauthorized Caller");
@@ -1417,7 +1415,7 @@
}
/**
- * This method should only be called by ConnectivityService. Because it doesn't
+ * This method should only be called by ConnectivityService because it doesn't
* have enough data to fill VpnInfo.primaryUnderlyingIface field.
*/
public synchronized VpnInfo getVpnInfo() {
@@ -1768,7 +1766,7 @@
* Bringing up a VPN connection takes time, and that is all this thread
* does. Here we have plenty of time. The only thing we need to take
* care of is responding to interruptions as soon as possible. Otherwise
- * requests will be piled up. This can be done in a Handler as a state
+ * requests will pile up. This could be done in a Handler as a state
* machine, but it is much easier to read in the current form.
*/
private class LegacyVpnRunner extends Thread {
@@ -1781,7 +1779,7 @@
private final AtomicInteger mOuterConnection =
new AtomicInteger(ConnectivityManager.TYPE_NONE);
- private long mTimer = -1;
+ private long mBringupStartTime = -1;
/**
* Watch for the outer connection (passing in the constructor) going away.
@@ -1861,8 +1859,8 @@
synchronized (TAG) {
Log.v(TAG, "Executing");
try {
- execute();
- monitorDaemons();
+ bringup();
+ waitForDaemonsToStop();
interrupted(); // Clear interrupt flag if execute called exit.
} catch (InterruptedException e) {
} finally {
@@ -1883,30 +1881,27 @@
}
}
- private void checkpoint(boolean yield) throws InterruptedException {
+ private void checkInterruptAndDelay(boolean sleepLonger) throws InterruptedException {
long now = SystemClock.elapsedRealtime();
- if (mTimer == -1) {
- mTimer = now;
- Thread.sleep(1);
- } else if (now - mTimer <= 60000) {
- Thread.sleep(yield ? 200 : 1);
+ if (now - mBringupStartTime <= 60000) {
+ Thread.sleep(sleepLonger ? 200 : 1);
} else {
updateState(DetailedState.FAILED, "checkpoint");
- throw new IllegalStateException("Time is up");
+ throw new IllegalStateException("VPN bringup took too long");
}
}
- private void execute() {
- // Catch all exceptions so we can clean up few things.
+ private void bringup() {
+ // Catch all exceptions so we can clean up a few things.
boolean initFinished = false;
try {
// Initialize the timer.
- checkpoint(false);
+ mBringupStartTime = SystemClock.elapsedRealtime();
// Wait for the daemons to stop.
for (String daemon : mDaemons) {
while (!SystemService.isStopped(daemon)) {
- checkpoint(true);
+ checkInterruptAndDelay(true);
}
}
@@ -1943,7 +1938,7 @@
// Wait for the daemon to start.
while (!SystemService.isRunning(daemon)) {
- checkpoint(true);
+ checkInterruptAndDelay(true);
}
// Create the control socket.
@@ -1959,7 +1954,7 @@
} catch (Exception e) {
// ignore
}
- checkpoint(true);
+ checkInterruptAndDelay(true);
}
mSockets[i].setSoTimeout(500);
@@ -1973,7 +1968,7 @@
out.write(bytes.length >> 8);
out.write(bytes.length);
out.write(bytes);
- checkpoint(false);
+ checkInterruptAndDelay(false);
}
out.write(0xFF);
out.write(0xFF);
@@ -1989,7 +1984,7 @@
} catch (Exception e) {
// ignore
}
- checkpoint(true);
+ checkInterruptAndDelay(true);
}
}
@@ -2002,7 +1997,7 @@
throw new IllegalStateException(daemon + " is dead");
}
}
- checkpoint(true);
+ checkInterruptAndDelay(true);
}
// Now we are connected. Read and parse the new state.
@@ -2058,8 +2053,8 @@
// Set the start time
mConfig.startTime = SystemClock.elapsedRealtime();
- // Check if the thread is interrupted while we are waiting.
- checkpoint(false);
+ // Check if the thread was interrupted while we were waiting on the lock.
+ checkInterruptAndDelay(false);
// Check if the interface is gone while we are waiting.
if (jniCheck(mConfig.interfaze) == 0) {
@@ -2082,10 +2077,11 @@
}
/**
- * Monitor the daemons we started, moving to disconnected state if the
- * underlying services fail.
+ * Check all daemons every two seconds. Return when one of them is stopped.
+ * The caller will move to the disconnected state when this function returns,
+ * which can happen if a daemon failed or if the VPN was torn down.
*/
- private void monitorDaemons() throws InterruptedException{
+ private void waitForDaemonsToStop() throws InterruptedException {
if (!mNetworkInfo.isConnected()) {
return;
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index 17adb1a..2224913 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -30,6 +30,7 @@
import android.net.ip.InterfaceController;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
+import android.net.util.InterfaceParams;
import android.net.util.NetdService;
import android.net.util.SharedLog;
import android.os.INetworkManagementService;
@@ -48,7 +49,6 @@
import java.net.Inet6Address;
import java.net.InetAddress;
-import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
@@ -120,8 +120,7 @@
private int mLastError;
private int mServingMode;
private String mMyUpstreamIfaceName; // may change over time
- private NetworkInterface mNetworkInterface;
- private byte[] mHwAddr;
+ private InterfaceParams mInterfaceParams;
// TODO: De-duplicate this with mLinkProperties above. Currently, these link
// properties are those selected by the IPv6TetheringCoordinator and relayed
// to us. By comparison, mLinkProperties contains the addresses and directly
@@ -247,31 +246,16 @@
}
private boolean startIPv6() {
- // TODO: Refactor for testability (perhaps passing an android.system.Os
- // instance and calling getifaddrs() directly).
- try {
- mNetworkInterface = NetworkInterface.getByName(mIfaceName);
- } catch (SocketException e) {
- mLog.e("Error looking up NetworkInterfaces: " + e);
- stopIPv6();
- return false;
- }
- if (mNetworkInterface == null) {
- mLog.e("Failed to find NetworkInterface");
+ // TODO: Refactor for better testability. This is one of the things
+ // that prohibits unittesting IPv6 tethering setup.
+ mInterfaceParams = InterfaceParams.getByName(mIfaceName);
+ if (mInterfaceParams == null) {
+ mLog.e("Failed to find InterfaceParams");
stopIPv6();
return false;
}
- try {
- mHwAddr = mNetworkInterface.getHardwareAddress();
- } catch (SocketException e) {
- mLog.e("Failed to find hardware address: " + e);
- stopIPv6();
- return false;
- }
-
- final int ifindex = mNetworkInterface.getIndex();
- mRaDaemon = new RouterAdvertisementDaemon(mIfaceName, ifindex, mHwAddr);
+ mRaDaemon = new RouterAdvertisementDaemon(mInterfaceParams);
if (!mRaDaemon.start()) {
stopIPv6();
return false;
@@ -281,8 +265,7 @@
}
private void stopIPv6() {
- mNetworkInterface = null;
- mHwAddr = null;
+ mInterfaceParams = null;
setRaParams(null);
if (mRaDaemon != null) {
diff --git a/services/core/java/com/android/server/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java
index d957ca0..51499f7 100644
--- a/services/core/java/com/android/server/content/SyncJobService.java
+++ b/services/core/java/com/android/server/content/SyncJobService.java
@@ -22,10 +22,12 @@
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.SparseLongArray;
import com.android.internal.annotations.GuardedBy;
@@ -44,6 +46,9 @@
@GuardedBy("mLock")
private final SparseBooleanArray mStartedSyncs = new SparseBooleanArray();
+ @GuardedBy("mLock")
+ private final SparseLongArray mJobStartUptimes = new SparseLongArray();
+
private final SyncLogger mLogger = SyncLogger.getInstance();
/**
@@ -82,7 +87,9 @@
synchronized (mLock) {
final int jobId = params.getJobId();
mJobParamsMap.put(jobId, params);
+
mStartedSyncs.delete(jobId);
+ mJobStartUptimes.put(jobId, SystemClock.uptimeMillis());
}
Message m = Message.obtain();
m.what = SyncManager.SyncHandler.MESSAGE_START_SYNC;
@@ -113,14 +120,32 @@
final int jobId = params.getJobId();
mJobParamsMap.remove(jobId);
- if (!mStartedSyncs.get(jobId)) {
- final String message = "Job " + jobId + " didn't start: params=" +
- jobParametersToString(params);
- mLogger.log(message);
- Slog.wtf(TAG, message);
+ final long startUptime = mJobStartUptimes.get(jobId);
+ final long nowUptime = SystemClock.uptimeMillis();
+ final long runtime = nowUptime - startUptime;
+
+ if (startUptime == 0) {
+ wtf("Job " + jobId + " start uptime not found: "
+ + " params=" + jobParametersToString(params));
+ } else if (runtime > 60 * 1000) {
+ // WTF if startSyncH() hasn't happened, *unless* onStopJob() was called too soon.
+ // (1 minute threshold.)
+ if (!mStartedSyncs.get(jobId)) {
+ wtf("Job " + jobId + " didn't start: "
+ + " startUptime=" + startUptime
+ + " nowUptime=" + nowUptime
+ + " params=" + jobParametersToString(params));
+ }
+ } else if (runtime < 10 * 1000) {
+ // Job stopped too soon. WTF.
+ wtf("Job " + jobId + " stopped in " + runtime + " ms: "
+ + " startUptime=" + startUptime
+ + " nowUptime=" + nowUptime
+ + " params=" + jobParametersToString(params));
}
mStartedSyncs.delete(jobId);
+ mJobStartUptimes.delete(jobId);
}
Message m = Message.obtain();
m.what = SyncManager.SyncHandler.MESSAGE_STOP_SYNC;
@@ -166,7 +191,13 @@
return "job:null";
} else {
return "job:#" + params.getJobId() + ":"
+ + "sr=[" + params.getStopReason() + "/" + params.getDebugStopReason() + "]:"
+ SyncOperation.maybeCreateFromJobExtras(params.getExtras());
}
}
+
+ private void wtf(String message) {
+ mLogger.log(message);
+ Slog.wtf(TAG, message);
+ }
}
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index cbb1c01..1e94e00 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -149,7 +149,7 @@
/**
* Start listening for brightness slider events
*
- * @param brightness the initial screen brightness
+ * @param initialBrightness the initial screen brightness
*/
public void start(float initialBrightness) {
if (DEBUG) {
@@ -219,8 +219,8 @@
if (includePackage) {
out.add(events[i]);
} else {
- BrightnessChangeEvent event = new BrightnessChangeEvent((events[i]));
- event.packageName = null;
+ BrightnessChangeEvent event = new BrightnessChangeEvent((events[i]),
+ /* redactPackage */ true);
out.add(event);
}
}
@@ -246,7 +246,8 @@
}
private void handleBrightnessChanged(float brightness, boolean userInitiated) {
- final BrightnessChangeEvent event;
+ BrightnessChangeEvent.Builder builder;
+
synchronized (mDataCollectionLock) {
if (!mStarted) {
// Not currently gathering brightness change information
@@ -263,9 +264,9 @@
return;
}
-
- event = new BrightnessChangeEvent();
- event.timeStamp = mInjector.currentTimeMillis();
+ builder = new BrightnessChangeEvent.Builder();
+ builder.setBrightness(brightness);
+ builder.setTimeStamp(mInjector.currentTimeMillis());
final int readingCount = mLastSensorReadings.size();
if (readingCount == 0) {
@@ -273,8 +274,8 @@
return;
}
- event.luxValues = new float[readingCount];
- event.luxTimestamps = new long[readingCount];
+ float[] luxValues = new float[readingCount];
+ long[] luxTimestamps = new long[readingCount];
int pos = 0;
@@ -282,33 +283,35 @@
long currentTimeMillis = mInjector.currentTimeMillis();
long elapsedTimeNanos = mInjector.elapsedRealtimeNanos();
for (LightData reading : mLastSensorReadings) {
- event.luxValues[pos] = reading.lux;
- event.luxTimestamps[pos] = currentTimeMillis -
+ luxValues[pos] = reading.lux;
+ luxTimestamps[pos] = currentTimeMillis -
TimeUnit.NANOSECONDS.toMillis(elapsedTimeNanos - reading.timestamp);
++pos;
}
+ builder.setLuxValues(luxValues);
+ builder.setLuxTimestamps(luxTimestamps);
- event.batteryLevel = mLastBatteryLevel;
- event.lastBrightness = previousBrightness;
+ builder.setBatteryLevel(mLastBatteryLevel);
+ builder.setLastBrightness(previousBrightness);
}
- event.brightness = brightness;
-
try {
final ActivityManager.StackInfo focusedStack = mInjector.getFocusedStack();
- event.userId = focusedStack.userId;
- event.packageName = focusedStack.topActivity.getPackageName();
+ builder.setUserId(focusedStack.userId);
+ builder.setPackageName(focusedStack.topActivity.getPackageName());
} catch (RemoteException e) {
// Really shouldn't be possible.
+ return;
}
- event.nightMode = mInjector.getSecureIntForUser(mContentResolver,
+ builder.setNightMode(mInjector.getSecureIntForUser(mContentResolver,
Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 0, UserHandle.USER_CURRENT)
- == 1;
- event.colorTemperature = mInjector.getSecureIntForUser(mContentResolver,
+ == 1);
+ builder.setColorTemperature(mInjector.getSecureIntForUser(mContentResolver,
Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE,
- 0, UserHandle.USER_CURRENT);
+ 0, UserHandle.USER_CURRENT));
+ BrightnessChangeEvent event = builder.build();
if (DEBUG) {
Slog.d(TAG, "Event " + event.brightness + " " + event.packageName);
}
@@ -457,40 +460,43 @@
}
tag = parser.getName();
if (TAG_EVENT.equals(tag)) {
- BrightnessChangeEvent event = new BrightnessChangeEvent();
+ BrightnessChangeEvent.Builder builder = new BrightnessChangeEvent.Builder();
String brightness = parser.getAttributeValue(null, ATTR_NITS);
- event.brightness = Float.parseFloat(brightness);
+ builder.setBrightness(Float.parseFloat(brightness));
String timestamp = parser.getAttributeValue(null, ATTR_TIMESTAMP);
- event.timeStamp = Long.parseLong(timestamp);
- event.packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
+ builder.setTimeStamp(Long.parseLong(timestamp));
+ builder.setPackageName(parser.getAttributeValue(null, ATTR_PACKAGE_NAME));
String user = parser.getAttributeValue(null, ATTR_USER);
- event.userId = mInjector.getUserId(mUserManager, Integer.parseInt(user));
+ builder.setUserId(mInjector.getUserId(mUserManager, Integer.parseInt(user)));
String batteryLevel = parser.getAttributeValue(null, ATTR_BATTERY_LEVEL);
- event.batteryLevel = Float.parseFloat(batteryLevel);
+ builder.setBatteryLevel(Float.parseFloat(batteryLevel));
String nightMode = parser.getAttributeValue(null, ATTR_NIGHT_MODE);
- event.nightMode = Boolean.parseBoolean(nightMode);
+ builder.setNightMode(Boolean.parseBoolean(nightMode));
String colorTemperature =
parser.getAttributeValue(null, ATTR_COLOR_TEMPERATURE);
- event.colorTemperature = Integer.parseInt(colorTemperature);
+ builder.setColorTemperature(Integer.parseInt(colorTemperature));
String lastBrightness = parser.getAttributeValue(null, ATTR_LAST_NITS);
- event.lastBrightness = Float.parseFloat(lastBrightness);
+ builder.setLastBrightness(Float.parseFloat(lastBrightness));
String luxValue = parser.getAttributeValue(null, ATTR_LUX);
String luxTimestamp = parser.getAttributeValue(null, ATTR_LUX_TIMESTAMPS);
- String[] luxValues = luxValue.split(",");
- String[] luxTimestamps = luxTimestamp.split(",");
- if (luxValues.length != luxTimestamps.length) {
+ String[] luxValuesStrings = luxValue.split(",");
+ String[] luxTimestampsStrings = luxTimestamp.split(",");
+ if (luxValuesStrings.length != luxTimestampsStrings.length) {
continue;
}
- event.luxValues = new float[luxValues.length];
- event.luxTimestamps = new long[luxValues.length];
+ float[] luxValues = new float[luxValuesStrings.length];
+ long[] luxTimestamps = new long[luxValuesStrings.length];
for (int i = 0; i < luxValues.length; ++i) {
- event.luxValues[i] = Float.parseFloat(luxValues[i]);
- event.luxTimestamps[i] = Long.parseLong(luxTimestamps[i]);
+ luxValues[i] = Float.parseFloat(luxValuesStrings[i]);
+ luxTimestamps[i] = Long.parseLong(luxTimestampsStrings[i]);
}
+ builder.setLuxValues(luxValues);
+ builder.setLuxTimestamps(luxTimestamps);
+ BrightnessChangeEvent event = builder.build();
if (DEBUG) {
Slog.i(TAG, "Read event " + event.brightness
+ " " + event.packageName);
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 85686ae..4f53ed4 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -99,7 +99,7 @@
private final float mProjMatrix[] = new float[16];
private final int[] mGLBuffers = new int[2];
private int mTexCoordLoc, mVertexLoc, mTexUnitLoc, mProjMatrixLoc, mTexMatrixLoc;
- private int mOpacityLoc, mGammaLoc, mSaturationLoc;
+ private int mOpacityLoc, mGammaLoc;
private int mProgram;
// Vertex and corresponding texture coordinates.
@@ -245,7 +245,6 @@
mOpacityLoc = GLES20.glGetUniformLocation(mProgram, "opacity");
mGammaLoc = GLES20.glGetUniformLocation(mProgram, "gamma");
- mSaturationLoc = GLES20.glGetUniformLocation(mProgram, "saturation");
mTexUnitLoc = GLES20.glGetUniformLocation(mProgram, "texUnit");
GLES20.glUseProgram(mProgram);
@@ -393,9 +392,8 @@
double cos = Math.cos(Math.PI * one_minus_level);
double sign = cos < 0 ? -1 : 1;
float opacity = (float) -Math.pow(one_minus_level, 2) + 1;
- float saturation = (float) Math.pow(level, 4);
float gamma = (float) ((0.5d * sign * Math.pow(cos, 2) + 0.5d) * 0.9d + 0.1d);
- drawFaded(opacity, 1.f / gamma, saturation);
+ drawFaded(opacity, 1.f / gamma);
if (checkGlErrors("drawFrame")) {
return false;
}
@@ -407,10 +405,9 @@
return showSurface(1.0f);
}
- private void drawFaded(float opacity, float gamma, float saturation) {
+ private void drawFaded(float opacity, float gamma) {
if (DEBUG) {
- Slog.d(TAG, "drawFaded: opacity=" + opacity + ", gamma=" + gamma +
- ", saturation=" + saturation);
+ Slog.d(TAG, "drawFaded: opacity=" + opacity + ", gamma=" + gamma);
}
// Use shaders
GLES20.glUseProgram(mProgram);
@@ -420,7 +417,6 @@
GLES20.glUniformMatrix4fv(mTexMatrixLoc, 1, false, mTexMatrix, 0);
GLES20.glUniform1f(mOpacityLoc, opacity);
GLES20.glUniform1f(mGammaLoc, gamma);
- GLES20.glUniform1f(mSaturationLoc, saturation);
// Use textures
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index fddb81b..6db3b44 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -19,6 +19,7 @@
import android.hardware.display.DisplayViewport;
import android.util.DisplayMetrics;
import android.view.Display;
+import android.view.DisplayCutout;
import android.view.Surface;
import java.util.Arrays;
@@ -229,6 +230,11 @@
public int flags;
/**
+ * The {@link DisplayCutout} if present or {@code null} otherwise.
+ */
+ public DisplayCutout displayCutout;
+
+ /**
* The touch attachment, per {@link DisplayViewport#touch}.
*/
public int touch;
@@ -321,6 +327,7 @@
|| appVsyncOffsetNanos != other.appVsyncOffsetNanos
|| presentationDeadlineNanos != other.presentationDeadlineNanos
|| flags != other.flags
+ || !Objects.equal(displayCutout, other.displayCutout)
|| touch != other.touch
|| rotation != other.rotation
|| type != other.type
@@ -354,6 +361,7 @@
appVsyncOffsetNanos = other.appVsyncOffsetNanos;
presentationDeadlineNanos = other.presentationDeadlineNanos;
flags = other.flags;
+ displayCutout = other.displayCutout;
touch = other.touch;
rotation = other.rotation;
type = other.type;
@@ -380,6 +388,9 @@
sb.append(", ").append(xDpi).append(" x ").append(yDpi).append(" dpi");
sb.append(", appVsyncOff ").append(appVsyncOffsetNanos);
sb.append(", presDeadline ").append(presentationDeadlineNanos);
+ if (displayCutout != null) {
+ sb.append(", cutout ").append(displayCutout);
+ }
sb.append(", touch ").append(touchToString(touch));
sb.append(", rotation ").append(rotation);
sb.append(", type ").append(Display.typeToString(type));
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 02e4fe0..a55fec5 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2009,5 +2009,14 @@
mDisplayPowerController.persistBrightnessSliderEvents();
}
}
+
+ @Override
+ public void onOverlayChanged() {
+ synchronized (mSyncRoot) {
+ if (updateLogicalDisplaysLocked()) {
+ scheduleTraversalLocked(false);
+ }
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index eb9ff58..23e4c9b 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -16,6 +16,7 @@
package com.android.server.display;
+import android.app.ActivityThread;
import android.content.res.Resources;
import com.android.server.LocalServices;
import com.android.server.lights.Light;
@@ -30,9 +31,12 @@
import android.os.PowerManager;
import android.os.SystemProperties;
import android.os.Trace;
+import android.text.TextUtils;
+import android.util.PathParser;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
+import android.view.DisplayCutout;
import android.view.DisplayEventReceiver;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -389,7 +393,7 @@
| DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
}
- final Resources res = getContext().getResources();
+ final Resources res = getOverlayContext().getResources();
if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
mInfo.name = res.getString(
com.android.internal.R.string.display_manager_built_in_display_name);
@@ -400,12 +404,14 @@
&& SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) {
mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND;
}
+ mInfo.displayCutout = parseDefaultDisplayCutout(res);
mInfo.type = Display.TYPE_BUILT_IN;
mInfo.densityDpi = (int)(phys.density * 160 + 0.5f);
mInfo.xDpi = phys.xDpi;
mInfo.yDpi = phys.yDpi;
mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
} else {
+ mInfo.displayCutout = null;
mInfo.type = Display.TYPE_HDMI;
mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION;
mInfo.name = getContext().getResources().getString(
@@ -434,6 +440,15 @@
return mInfo;
}
+ private DisplayCutout parseDefaultDisplayCutout(Resources res) {
+ String cutoutString = res.getString(
+ com.android.internal.R.string.config_mainBuiltInDisplayCutout);
+ if (TextUtils.isEmpty(cutoutString)) {
+ return null;
+ }
+ return DisplayCutout.fromBounds(PathParser.createPathFromPathData(cutoutString));
+ }
+
@Override
public Runnable requestDisplayStateLocked(final int state, final int brightness) {
// Assume that the brightness is off if the display is being turned off.
@@ -673,6 +688,11 @@
}
}
+ /** Supplies a context whose Resources apply runtime-overlays */
+ Context getOverlayContext() {
+ return ActivityThread.currentActivityThread().getSystemUiContext();
+ }
+
/**
* Keeps track of a display configuration.
*/
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 78a5407..132f083 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -145,6 +145,7 @@
mInfo.overscanRight = mOverrideDisplayInfo.overscanRight;
mInfo.overscanBottom = mOverrideDisplayInfo.overscanBottom;
mInfo.rotation = mOverrideDisplayInfo.rotation;
+ mInfo.displayCutout = mOverrideDisplayInfo.displayCutout;
mInfo.logicalDensityDpi = mOverrideDisplayInfo.logicalDensityDpi;
mInfo.physicalXDpi = mOverrideDisplayInfo.physicalXDpi;
mInfo.physicalYDpi = mOverrideDisplayInfo.physicalYDpi;
@@ -280,6 +281,7 @@
mBaseDisplayInfo.largestNominalAppHeight = deviceInfo.height;
mBaseDisplayInfo.ownerUid = deviceInfo.ownerUid;
mBaseDisplayInfo.ownerPackageName = deviceInfo.ownerPackageName;
+ mBaseDisplayInfo.displayCutout = deviceInfo.displayCutout;
mPrimaryDisplayDeviceInfo = deviceInfo;
mInfo = null;
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 733ed3d..bd1dbf9 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -934,12 +934,14 @@
* @param uid Uid of the calling client.
* @param jobId Id of the job, provided at schedule-time.
*/
- public boolean cancelJob(int uid, int jobId) {
+ public boolean cancelJob(int uid, int jobId, int callingUid) {
JobStatus toCancel;
synchronized (mLock) {
toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
if (toCancel != null) {
- cancelJobImplLocked(toCancel, null, "cancel() called by app");
+ cancelJobImplLocked(toCancel, null,
+ "cancel() called by app, callingUid=" + callingUid
+ + " uid=" + uid + " jobId=" + jobId);
}
return (toCancel != null);
}
@@ -2341,7 +2343,8 @@
final int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
- JobSchedulerService.this.cancelJobsForUid(uid, "cancelAll() called by app");
+ JobSchedulerService.this.cancelJobsForUid(uid,
+ "cancelAll() called by app, callingUid=" + uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2353,7 +2356,7 @@
long ident = Binder.clearCallingIdentity();
try {
- JobSchedulerService.this.cancelJob(uid, jobId);
+ JobSchedulerService.this.cancelJob(uid, jobId, uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2466,7 +2469,7 @@
for (int i=0; i<mActiveServices.size(); i++) {
final JobServiceContext jc = mActiveServices.get(i);
final JobStatus js = jc.getRunningJobLocked();
- if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId)) {
+ if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) {
foundSome = true;
pw.print("Timing out: ");
js.printUniqueId(pw);
@@ -2506,7 +2509,7 @@
}
} else {
pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId);
- if (!cancelJob(pkgUid, jobId)) {
+ if (!cancelJob(pkgUid, jobId, Process.SHELL_UID)) {
pw.println("No matching job found.");
}
}
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index 709deeb..83a3c19 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -312,13 +312,14 @@
return mTimeoutElapsed;
}
- boolean timeoutIfExecutingLocked(String pkgName, int userId, boolean matchJobId, int jobId) {
+ boolean timeoutIfExecutingLocked(String pkgName, int userId, boolean matchJobId, int jobId,
+ String reason) {
final JobStatus executing = getRunningJobLocked();
if (executing != null && (userId == UserHandle.USER_ALL || userId == executing.getUserId())
&& (pkgName == null || pkgName.equals(executing.getSourcePackageName()))
&& (!matchJobId || jobId == executing.getJobId())) {
if (mVerb == VERB_EXECUTING) {
- mParams.setStopReason(JobParameters.REASON_TIMEOUT);
+ mParams.setStopReason(JobParameters.REASON_TIMEOUT, reason);
sendStopMessageLocked("force timeout from shell");
return true;
}
@@ -537,7 +538,7 @@
}
return;
}
- mParams.setStopReason(arg1);
+ mParams.setStopReason(arg1, debugReason);
if (arg1 == JobParameters.REASON_PREEMPT) {
mPreferredUid = mRunningJob != null ? mRunningJob.getUid() :
NO_PREFERRED_UID;
@@ -687,7 +688,7 @@
// Not an error - client ran out of time.
Slog.i(TAG, "Client timed out while executing (no jobFinished received), " +
"sending onStop: " + getRunningJobNameLocked());
- mParams.setStopReason(JobParameters.REASON_TIMEOUT);
+ mParams.setStopReason(JobParameters.REASON_TIMEOUT, "client timed out");
sendStopMessageLocked("timeout while executing");
break;
default:
diff --git a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
index 5f95f1a..1d053a5 100644
--- a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -17,14 +17,11 @@
package com.android.server.job.controllers;
import android.content.Context;
-import android.os.IDeviceIdleController;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.util.ArrayUtils;
import com.android.server.ForceAppStandbyTracker;
import com.android.server.ForceAppStandbyTracker.Listener;
import com.android.server.job.JobSchedulerService;
@@ -44,7 +41,6 @@
private static volatile BackgroundJobsController sController;
private final JobSchedulerService mJobSchedulerService;
- private final IDeviceIdleController mDeviceIdleController;
private final ForceAppStandbyTracker mForceAppStandbyTracker;
@@ -62,8 +58,6 @@
private BackgroundJobsController(JobSchedulerService service, Context context, Object lock) {
super(service, context, lock);
mJobSchedulerService = service;
- mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
- ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context);
@@ -235,17 +229,23 @@
private final Listener mForceAppStandbyListener = new Listener() {
@Override
public void updateAllJobs() {
- updateAllJobRestrictionsLocked();
+ synchronized (mLock) {
+ updateAllJobRestrictionsLocked();
+ }
}
@Override
public void updateJobsForUid(int uid) {
- updateJobRestrictionsForUidLocked(uid);
+ synchronized (mLock) {
+ updateJobRestrictionsForUidLocked(uid);
+ }
}
@Override
public void updateJobsForUidPackage(int uid, String packageName) {
- updateJobRestrictionsForUidLocked(uid);
+ synchronized (mLock) {
+ updateJobRestrictionsForUidLocked(uid);
+ }
}
};
}
diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java
index 60b5b1f..a61842b 100644
--- a/services/core/java/com/android/server/location/ContextHubClientManager.java
+++ b/services/core/java/com/android/server/location/ContextHubClientManager.java
@@ -113,9 +113,7 @@
NanoAppMessage clientMessage = ContextHubServiceUtil.createNanoAppMessage(message);
if (DEBUG_LOG_ENABLED) {
- String targetAudience = clientMessage.isBroadcastMessage() ? "broadcast" : "unicast";
- Log.v(TAG, "Received a " + targetAudience + " message from nanoapp 0x"
- + Long.toHexString(clientMessage.getNanoAppId()));
+ Log.v(TAG, "Received " + clientMessage);
}
if (clientMessage.isBroadcastMessage()) {
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index e6de07d..e158819 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -84,8 +84,6 @@
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
-import com.android.server.power.BatterySaverPolicy;
-
import libcore.io.IoUtils;
import java.io.File;
@@ -579,7 +577,7 @@
final PowerSaveState result =
mPowerManager.getPowerSaveState(ServiceType.GPS);
switch (result.gpsMode) {
- case BatterySaverPolicy.GPS_MODE_DISABLED_WHEN_SCREEN_OFF:
+ case PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF:
// If we are in battery saver mode and the screen is off, disable GPS.
disableGps |= result.batterySaverEnabled && !mPowerManager.isInteractive();
break;
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 516828b..7bd1ae9 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -63,7 +63,6 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
-import android.os.ServiceSpecificException;
import android.os.ShellCallback;
import android.os.StrictMode;
import android.os.SystemProperties;
@@ -78,11 +77,10 @@
import android.security.keystore.AndroidKeyStoreProvider;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
+import android.security.keystore.KeychainProtectionParams;
import android.security.keystore.UserNotAuthenticatedException;
-import android.security.recoverablekeystore.KeyEntryRecoveryData;
-import android.security.recoverablekeystore.KeyStoreRecoveryData;
-import android.security.recoverablekeystore.KeyStoreRecoveryMetadata;
-import android.security.recoverablekeystore.RecoverableKeyStoreLoader.RecoverableKeyStoreLoaderException;
+import android.security.keystore.WrappedApplicationKey;
+import android.security.keystore.KeychainSnapshot;
import android.service.gatekeeper.GateKeeperResponse;
import android.service.gatekeeper.IGateKeeperService;
import android.text.TextUtils;
@@ -90,6 +88,7 @@
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -1874,6 +1873,7 @@
mSpManager.removeUser(userId);
mStorage.removeUser(userId);
mStrongAuth.removeUser(userId);
+ cleanSpCache();
final KeyStore ks = KeyStore.getInstance();
ks.onUserRemoved(userId);
@@ -1968,7 +1968,7 @@
}
@Override
- public KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account) throws RemoteException {
+ public KeychainSnapshot getRecoveryData(@NonNull byte[] account) throws RemoteException {
return mRecoverableKeyStoreManager.getRecoveryData(account);
}
@@ -1982,8 +1982,8 @@
}
@Override
- public void setServerParameters(long serverParameters) throws RemoteException {
- mRecoverableKeyStoreManager.setServerParameters(serverParameters);
+ public void setServerParams(byte[] serverParams) throws RemoteException {
+ mRecoverableKeyStoreManager.setServerParams(serverParams);
}
@Override
@@ -1997,7 +1997,7 @@
}
@Override
- public void setRecoverySecretTypes(@NonNull @KeyStoreRecoveryMetadata.UserSecretType
+ public void setRecoverySecretTypes(@NonNull @KeychainProtectionParams.UserSecretType
int[] secretTypes) throws RemoteException {
mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
}
@@ -2014,7 +2014,7 @@
}
@Override
- public void recoverySecretAvailable(@NonNull KeyStoreRecoveryMetadata recoverySecret)
+ public void recoverySecretAvailable(@NonNull KeychainProtectionParams recoverySecret)
throws RemoteException {
mRecoverableKeyStoreManager.recoverySecretAvailable(recoverySecret);
}
@@ -2022,7 +2022,7 @@
@Override
public byte[] startRecoverySession(@NonNull String sessionId,
@NonNull byte[] verifierPublicKey, @NonNull byte[] vaultParams,
- @NonNull byte[] vaultChallenge, @NonNull List<KeyStoreRecoveryMetadata> secrets)
+ @NonNull byte[] vaultChallenge, @NonNull List<KeychainProtectionParams> secrets)
throws RemoteException {
return mRecoverableKeyStoreManager.startRecoverySession(sessionId, verifierPublicKey,
vaultParams, vaultChallenge, secrets);
@@ -2030,7 +2030,7 @@
@Override
public Map<String, byte[]> recoverKeys(@NonNull String sessionId,
- @NonNull byte[] recoveryKeyBlob, @NonNull List<KeyEntryRecoveryData> applicationKeys)
+ @NonNull byte[] recoveryKeyBlob, @NonNull List<WrappedApplicationKey> applicationKeys)
throws RemoteException {
return mRecoverableKeyStoreManager.recoverKeys(
sessionId, recoveryKeyBlob, applicationKeys);
@@ -2112,6 +2112,63 @@
}
/**
+ * A user's synthetic password does not change so it must be cached in certain circumstances to
+ * enable untrusted credential reset.
+ *
+ * Untrusted credential reset will be removed in a future version (b/68036371) at which point
+ * this cache is no longer needed as the SP will always be known when changing the user's
+ * credential.
+ */
+ @GuardedBy("mSpManager")
+ private SparseArray<AuthenticationToken> mSpCache = new SparseArray();
+
+ private void onAuthTokenKnownForUser(@UserIdInt int userId, AuthenticationToken auth) {
+ // Update the SP cache, removing the entry when allowed
+ synchronized (mSpManager) {
+ if (shouldCacheSpForUser(userId)) {
+ Slog.i(TAG, "Caching SP for user " + userId);
+ mSpCache.put(userId, auth);
+ } else {
+ Slog.i(TAG, "Not caching SP for user " + userId);
+ mSpCache.delete(userId);
+ }
+ }
+ }
+
+ /** Clean up the SP cache by removing unneeded entries. */
+ private void cleanSpCache() {
+ synchronized (mSpManager) {
+ // Preserve indicies after removal by iterating backwards
+ for (int i = mSpCache.size() - 1; i >= 0; --i) {
+ final int userId = mSpCache.keyAt(i);
+ if (!shouldCacheSpForUser(userId)) {
+ Slog.i(TAG, "Uncaching SP for user " + userId);
+ mSpCache.removeAt(i);
+ }
+ }
+ }
+ }
+
+ private boolean shouldCacheSpForUser(@UserIdInt int userId) {
+ // Before the user setup has completed, an admin could be installed that requires the SP to
+ // be cached (see below).
+ if (Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0, userId) == 0) {
+ return true;
+ }
+
+ // If the user has an admin which can perform an untrusted credential reset, the SP needs to
+ // be cached. If there isn't a DevicePolicyManager then there can't be an admin in the first
+ // place so caching is not necessary.
+ final DevicePolicyManagerInternal dpmi = LocalServices.getService(
+ DevicePolicyManagerInternal.class);
+ if (dpmi == null) {
+ return false;
+ }
+ return dpmi.canUserHaveUntrustedCredentialReset(userId);
+ }
+
+ /**
* Precondition: vold and keystore unlocked.
*
* Create new synthetic password, set up synthetic password blob protected by the supplied
@@ -2126,9 +2183,7 @@
* 3. Once a user is migrated to have synthetic password, its value will never change, no matter
* whether the user changes his lockscreen PIN or clear/reset it. When the user clears its
* lockscreen PIN, we still maintain the existing synthetic password in a password blob
- * protected by a default PIN. The only exception is when the DPC performs an untrusted
- * credential change, in which case we have no way to derive the existing synthetic password
- * and has to create a new one.
+ * protected by a default PIN.
* 4. The user SID is linked with synthetic password, but its cleared/re-created when the user
* clears/re-creates his lockscreen PIN.
*
@@ -2148,13 +2203,23 @@
* This is the untrusted credential reset, OR the user sets a new lockscreen password
* FOR THE FIRST TIME on a SP-enabled device. New credential and new SID will be created
*/
+ @GuardedBy("mSpManager")
@VisibleForTesting
protected AuthenticationToken initializeSyntheticPasswordLocked(byte[] credentialHash,
String credential, int credentialType, int requestedQuality,
int userId) throws RemoteException {
Slog.i(TAG, "Initialize SyntheticPassword for user: " + userId);
- AuthenticationToken auth = mSpManager.newSyntheticPasswordAndSid(getGateKeeperService(),
- credentialHash, credential, userId);
+ // Load from the cache or a make a new one
+ AuthenticationToken auth = mSpCache.get(userId);
+ if (auth != null) {
+ // If the synthetic password has been cached, we can only be in case 3., described
+ // above, for an untrusted credential reset so a new SID is still needed.
+ mSpManager.newSidForUser(getGateKeeperService(), auth, userId);
+ } else {
+ auth = mSpManager.newSyntheticPasswordAndSid(getGateKeeperService(),
+ credentialHash, credential, userId);
+ }
+ onAuthTokenKnownForUser(userId, auth);
if (auth == null) {
Slog.wtf(TAG, "initializeSyntheticPasswordLocked returns null auth token");
return null;
@@ -2269,6 +2334,8 @@
trustManager.setDeviceLockedForUser(userId, false);
}
mStrongAuth.reportSuccessfulStrongAuthUnlock(userId);
+
+ onAuthTokenKnownForUser(userId, authResult.authToken);
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
if (response.getTimeout() > 0) {
requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, userId);
@@ -2287,6 +2354,7 @@
* SID is gone. We also clear password from (software-based) keystore and vold, which will be
* added back when new password is set in future.
*/
+ @GuardedBy("mSpManager")
private long setLockCredentialWithAuthTokenLocked(String credential, int credentialType,
AuthenticationToken auth, int requestedQuality, int userId) throws RemoteException {
if (DEBUG) Slog.d(TAG, "setLockCredentialWithAuthTokenLocked: user=" + userId);
@@ -2334,6 +2402,7 @@
return newHandle;
}
+ @GuardedBy("mSpManager")
private void spBasedSetLockCredentialInternalLocked(String credential, int credentialType,
String savedCredential, int requestedQuality, int userId) throws RemoteException {
if (DEBUG) Slog.d(TAG, "spBasedSetLockCredentialInternalLocked: user=" + userId);
@@ -2369,13 +2438,19 @@
setLockCredentialWithAuthTokenLocked(credential, credentialType, auth, requestedQuality,
userId);
mSpManager.destroyPasswordBasedSyntheticPassword(handle, userId);
+ onAuthTokenKnownForUser(userId, auth);
} else if (response != null
- && response.getResponseCode() == VerifyCredentialResponse.RESPONSE_ERROR){
+ && response.getResponseCode() == VerifyCredentialResponse.RESPONSE_ERROR) {
// We are performing an untrusted credential change i.e. by DevicePolicyManager.
// So provision a new SP and SID. This would invalidate existing escrow tokens.
// Still support this for now but this flow will be removed in the next release.
-
Slog.w(TAG, "Untrusted credential change invoked");
+
+ if (mSpCache.get(userId) == null) {
+ throw new IllegalStateException(
+ "Untrusted credential reset not possible without cached SP");
+ }
+
initializeSyntheticPasswordLocked(null, credential, credentialType, requestedQuality,
userId);
synchronizeUnifiedWorkChallengeForProfiles(userId, null);
@@ -2486,8 +2561,9 @@
private boolean setLockCredentialWithTokenInternal(String credential, int type,
long tokenHandle, byte[] token, int requestedQuality, int userId) throws RemoteException {
+ final AuthenticationResult result;
synchronized (mSpManager) {
- AuthenticationResult result = mSpManager.unwrapTokenBasedSyntheticPassword(
+ result = mSpManager.unwrapTokenBasedSyntheticPassword(
getGateKeeperService(), tokenHandle, token, userId);
if (result.authToken == null) {
Slog.w(TAG, "Invalid escrow token supplied");
@@ -2508,8 +2584,9 @@
setLockCredentialWithAuthTokenLocked(credential, type, result.authToken,
requestedQuality, userId);
mSpManager.destroyPasswordBasedSyntheticPassword(oldHandle, userId);
- return true;
}
+ onAuthTokenKnownForUser(userId, result.authToken);
+ return true;
}
@Override
@@ -2529,6 +2606,7 @@
}
}
unlockUser(userId, null, authResult.authToken.deriveDiskEncryptionKey());
+ onAuthTokenKnownForUser(userId, authResult.authToken);
}
@Override
@@ -2610,6 +2688,8 @@
private class DeviceProvisionedObserver extends ContentObserver {
private final Uri mDeviceProvisionedUri = Settings.Global.getUriFor(
Settings.Global.DEVICE_PROVISIONED);
+ private final Uri mUserSetupCompleteUri = Settings.Secure.getUriFor(
+ Settings.Secure.USER_SETUP_COMPLETE);
private boolean mRegistered;
@@ -2627,6 +2707,8 @@
reportDeviceSetupComplete();
clearFrpCredentialIfOwnerNotSecure();
}
+ } else if (mUserSetupCompleteUri.equals(uri)) {
+ cleanSpCache();
}
}
@@ -2678,6 +2760,8 @@
if (register) {
mContext.getContentResolver().registerContentObserver(mDeviceProvisionedUri,
false, this);
+ mContext.getContentResolver().registerContentObserver(mUserSetupCompleteUri,
+ false, this, UserHandle.USER_ALL);
} else {
mContext.getContentResolver().unregisterContentObserver(this);
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index 2c8bc4e..a29e1be 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -16,18 +16,18 @@
package com.android.server.locksettings.recoverablekeystore;
-import static android.security.recoverablekeystore.KeyStoreRecoveryMetadata.TYPE_LOCKSCREEN;
+import static android.security.keystore.KeychainProtectionParams.TYPE_LOCKSCREEN;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.security.recoverablekeystore.KeyDerivationParameters;
-import android.security.recoverablekeystore.KeyEntryRecoveryData;
-import android.security.recoverablekeystore.KeyStoreRecoveryData;
-import android.security.recoverablekeystore.KeyStoreRecoveryMetadata;
+import android.security.keystore.KeyDerivationParams;
+import android.security.keystore.KeychainProtectionParams;
+import android.security.keystore.KeychainSnapshot;
+import android.security.keystore.WrappedApplicationKey;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
@@ -174,8 +174,8 @@
return;
}
- Long deviceId = mRecoverableKeyStoreDb.getServerParameters(mUserId, recoveryAgentUid);
- if (deviceId == null) {
+ byte[] vaultHandle = mRecoverableKeyStoreDb.getServerParams(mUserId, recoveryAgentUid);
+ if (vaultHandle == null) {
Log.w(TAG, "No device ID set for user " + mUserId);
return;
}
@@ -232,7 +232,7 @@
publicKey,
counterId,
TRUSTED_HARDWARE_MAX_ATTEMPTS,
- deviceId);
+ vaultHandle);
byte[] encryptedRecoveryKey;
try {
@@ -249,12 +249,13 @@
return;
}
// TODO: store raw data in RecoveryServiceMetadataEntry and generate Parcelables later
- KeyStoreRecoveryMetadata metadata = new KeyStoreRecoveryMetadata(
+ // TODO: use Builder.
+ KeychainProtectionParams metadata = new KeychainProtectionParams(
/*userSecretType=*/ TYPE_LOCKSCREEN,
/*lockScreenUiFormat=*/ getUiFormat(mCredentialType, mCredential),
- /*keyDerivationParameters=*/ KeyDerivationParameters.createSha256Parameters(salt),
+ /*keyDerivationParams=*/ KeyDerivationParams.createSha256Params(salt),
/*secret=*/ new byte[0]);
- ArrayList<KeyStoreRecoveryMetadata> metadataList = new ArrayList<>();
+ ArrayList<KeychainProtectionParams> metadataList = new ArrayList<>();
metadataList.add(metadata);
int snapshotVersion = incrementSnapshotVersion(recoveryAgentUid);
@@ -262,7 +263,8 @@
// If application keys are not updated, snapshot will not be created on next unlock.
mRecoverableKeyStoreDb.setShouldCreateSnapshot(mUserId, recoveryAgentUid, false);
- mRecoverySnapshotStorage.put(recoveryAgentUid, new KeyStoreRecoveryData(
+ // TODO: use Builder.
+ mRecoverySnapshotStorage.put(recoveryAgentUid, new KeychainSnapshot(
snapshotVersion,
/*recoveryMetadata=*/ metadataList,
/*applicationKeyBlobs=*/ createApplicationKeyEntries(encryptedApplicationKeys),
@@ -304,6 +306,12 @@
* @param recoveryAgentUid uid of the recovery agent.
*/
private boolean shoudCreateSnapshot(int recoveryAgentUid) {
+ int[] types = mRecoverableKeyStoreDb.getRecoverySecretTypes(mUserId, recoveryAgentUid);
+ if (!ArrayUtils.contains(types, KeychainProtectionParams.TYPE_LOCKSCREEN)) {
+ // Only lockscreen type is supported.
+ // We will need to pass extra argument to KeySyncTask to support custom pass phrase.
+ return false;
+ }
if (mCredentialUpdated) {
// Sync credential if at least one snapshot was created.
if (mRecoverableKeyStoreDb.getSnapshotVersion(mUserId, recoveryAgentUid) != null) {
@@ -322,14 +330,14 @@
* @return The format - either pattern, pin, or password.
*/
@VisibleForTesting
- @KeyStoreRecoveryMetadata.LockScreenUiFormat static int getUiFormat(
+ @KeychainProtectionParams.LockScreenUiFormat static int getUiFormat(
int credentialType, String credential) {
if (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) {
- return KeyStoreRecoveryMetadata.TYPE_PATTERN;
+ return KeychainProtectionParams.TYPE_PATTERN;
} else if (isPin(credential)) {
- return KeyStoreRecoveryMetadata.TYPE_PIN;
+ return KeychainProtectionParams.TYPE_PIN;
} else {
- return KeyStoreRecoveryMetadata.TYPE_PASSWORD;
+ return KeychainProtectionParams.TYPE_PASSWORD;
}
}
@@ -392,12 +400,12 @@
return keyGenerator.generateKey();
}
- private static List<KeyEntryRecoveryData> createApplicationKeyEntries(
+ private static List<WrappedApplicationKey> createApplicationKeyEntries(
Map<String, byte[]> encryptedApplicationKeys) {
- ArrayList<KeyEntryRecoveryData> keyEntries = new ArrayList<>();
+ ArrayList<WrappedApplicationKey> keyEntries = new ArrayList<>();
for (String alias : encryptedApplicationKeys.keySet()) {
keyEntries.add(
- new KeyEntryRecoveryData(
+ new WrappedApplicationKey(
alias,
encryptedApplicationKeys.get(alias)));
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
index e851d8c..219f6e1 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
@@ -37,8 +37,7 @@
import javax.crypto.SecretKey;
/**
- * Utility functions for the flow where the RecoverableKeyStoreLoader syncs keys with remote
- * storage.
+ * Utility functions for the flow where the RecoveryManager syncs keys with remote storage.
*
* @hide
*/
@@ -62,7 +61,8 @@
private static final byte[] THM_KF_HASH_PREFIX = "THM_KF_hash".getBytes(StandardCharsets.UTF_8);
private static final int KEY_CLAIMANT_LENGTH_BYTES = 16;
- private static final int VAULT_PARAMS_LENGTH_BYTES = 85;
+ private static final int VAULT_PARAMS_LENGTH_BYTES = 94;
+ private static final int VAULT_HANDLE_LENGTH_BYTES = 17;
/**
* Encrypts the recovery key using both the lock screen hash and the remote storage's public
@@ -289,17 +289,18 @@
* @param thmPublicKey Public key of the trusted hardware module.
* @param counterId ID referring to the specific counter in the hardware module.
* @param maxAttempts Maximum allowed guesses before trusted hardware wipes key.
- * @param deviceId ID of the device.
+ * @param vaultHandle Handle of the Vault.
* @return The binary vault params, ready for sync.
*/
public static byte[] packVaultParams(
- PublicKey thmPublicKey, long counterId, int maxAttempts, long deviceId) {
+ PublicKey thmPublicKey, long counterId, int maxAttempts, byte[] vaultHandle) {
+ // TODO: Check if vaultHandle has exactly the length of VAULT_HANDLE_LENGTH_BYTES somewhere
return ByteBuffer.allocate(VAULT_PARAMS_LENGTH_BYTES)
.order(ByteOrder.LITTLE_ENDIAN)
.put(SecureBox.encodePublicKey(thmPublicKey))
.putLong(counterId)
- .putLong(deviceId)
.putInt(maxAttempts)
+ .put(new byte[VAULT_HANDLE_LENGTH_BYTES]) // TODO: replace with real vaultHandle
.array();
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index a6f7766..801dc54 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -16,32 +16,27 @@
package com.android.server.locksettings.recoverablekeystore;
-import static android.security.recoverablekeystore.RecoverableKeyStoreLoader
- .ERROR_BAD_X509_CERTIFICATE;
-import static android.security.recoverablekeystore.RecoverableKeyStoreLoader.ERROR_DATABASE_ERROR;
-import static android.security.recoverablekeystore.RecoverableKeyStoreLoader
- .ERROR_DECRYPTION_FAILED;
-import static android.security.recoverablekeystore.RecoverableKeyStoreLoader.ERROR_INSECURE_USER;
-import static android.security.recoverablekeystore.RecoverableKeyStoreLoader
- .ERROR_KEYSTORE_INTERNAL_ERROR;
-import static android.security.recoverablekeystore.RecoverableKeyStoreLoader
- .ERROR_NOT_YET_SUPPORTED;
-import static android.security.recoverablekeystore.RecoverableKeyStoreLoader
- .ERROR_UNEXPECTED_MISSING_ALGORITHM;
+import static android.security.keystore.RecoveryManager.ERROR_BAD_CERTIFICATE_FORMAT;
+import static android.security.keystore.RecoveryManager.ERROR_DECRYPTION_FAILED;
+import static android.security.keystore.RecoveryManager.ERROR_INSECURE_USER;
+import static android.security.keystore.RecoveryManager.ERROR_NO_SNAPSHOT_PENDING;
+import static android.security.keystore.RecoveryManager.ERROR_SERVICE_INTERNAL_ERROR;
+import static android.security.keystore.RecoveryManager.ERROR_SESSION_EXPIRED;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
+import android.Manifest;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
-import android.security.recoverablekeystore.KeyEntryRecoveryData;
-import android.security.recoverablekeystore.KeyStoreRecoveryData;
-import android.security.recoverablekeystore.KeyStoreRecoveryMetadata;
-import android.security.recoverablekeystore.RecoverableKeyStoreLoader;
+import android.security.keystore.KeychainProtectionParams;
+import android.security.keystore.KeychainSnapshot;
+import android.security.keystore.WrappedApplicationKey;
+import android.security.keystore.RecoveryManager;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -49,7 +44,6 @@
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
-import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyStoreException;
import java.security.KeyFactory;
@@ -69,7 +63,7 @@
import javax.crypto.AEADBadTagException;
/**
- * Class with {@link RecoverableKeyStoreLoader} API implementation and internal methods to interact
+ * Class with {@link RecoveryManager} API implementation and internal methods to interact
* with {@code LockSettingsService}.
*
* @hide
@@ -103,7 +97,7 @@
// Impossible: all algorithms must be supported by AOSP
throw new RuntimeException(e);
} catch (KeyStoreException e) {
- throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR, e.getMessage());
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
}
mInstance = new RecoverableKeyStoreManager(
@@ -139,7 +133,7 @@
mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase);
} catch (NoSuchAlgorithmException e) {
Log.wtf(TAG, "AES keygen algorithm not available. AOSP must support this.", e);
- throw new ServiceSpecificException(ERROR_UNEXPECTED_MISSING_ALGORITHM, e.getMessage());
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
}
}
@@ -148,6 +142,7 @@
throws RemoteException {
checkRecoverKeyStorePermission();
int userId = UserHandle.getCallingUserId();
+ int uid = Binder.getCallingUid();
// TODO: open /system/etc/security/... cert file, and check the signature on the public keys
PublicKey publicKey;
try {
@@ -157,12 +152,15 @@
publicKey = kf.generatePublic(pkSpec);
} catch (NoSuchAlgorithmException e) {
Log.wtf(TAG, "EC algorithm not available. AOSP must support this.", e);
- throw new ServiceSpecificException(ERROR_UNEXPECTED_MISSING_ALGORITHM, e.getMessage());
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
} catch (InvalidKeySpecException e) {
throw new ServiceSpecificException(
- ERROR_BAD_X509_CERTIFICATE, "Not a valid X509 certificate.");
+ ERROR_BAD_CERTIFICATE_FORMAT, "Not a valid X509 certificate.");
}
- mDatabase.setRecoveryServicePublicKey(userId, Binder.getCallingUid(), publicKey);
+ long updatedRows = mDatabase.setRecoveryServicePublicKey(userId, uid, publicKey);
+ if (updatedRows > 0) {
+ mDatabase.setShouldCreateSnapshot(userId, uid, true);
+ }
}
/**
@@ -171,13 +169,14 @@
* @return recovery data
* @hide
*/
- public @NonNull KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account)
+ public @NonNull
+ KeychainSnapshot getRecoveryData(@NonNull byte[] account)
throws RemoteException {
checkRecoverKeyStorePermission();
int uid = Binder.getCallingUid();
- KeyStoreRecoveryData snapshot = mSnapshotStorage.get(uid);
+ KeychainSnapshot snapshot = mSnapshotStorage.get(uid);
if (snapshot == null) {
- throw new ServiceSpecificException(RecoverableKeyStoreLoader.ERROR_NO_SNAPSHOT_PENDING);
+ throw new ServiceSpecificException(ERROR_NO_SNAPSHOT_PENDING);
}
return snapshot;
}
@@ -201,10 +200,14 @@
throw new UnsupportedOperationException();
}
- public void setServerParameters(long serverParameters) throws RemoteException {
+ public void setServerParams(byte[] serverParams) throws RemoteException {
checkRecoverKeyStorePermission();
int userId = UserHandle.getCallingUserId();
- mDatabase.setServerParameters(userId, Binder.getCallingUid(), serverParameters);
+ int uid = Binder.getCallingUid();
+ long updatedRows = mDatabase.setServerParams(userId, uid, serverParams);
+ if (updatedRows > 0) {
+ mDatabase.setShouldCreateSnapshot(userId, uid, true);
+ }
}
/**
@@ -253,11 +256,15 @@
* @hide
*/
public void setRecoverySecretTypes(
- @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] secretTypes)
+ @NonNull @KeychainProtectionParams.UserSecretType int[] secretTypes)
throws RemoteException {
checkRecoverKeyStorePermission();
- mDatabase.setRecoverySecretTypes(UserHandle.getCallingUserId(), Binder.getCallingUid(),
- secretTypes);
+ int userId = UserHandle.getCallingUserId();
+ int uid = Binder.getCallingUid();
+ long updatedRows = mDatabase.setRecoverySecretTypes(userId, uid, secretTypes);
+ if (updatedRows > 0) {
+ mDatabase.setShouldCreateSnapshot(userId, uid, true);
+ }
}
/**
@@ -273,7 +280,7 @@
}
/**
- * Gets secret types RecoverableKeyStoreLoaders is waiting for to create new Recovery Data.
+ * Gets secret types RecoveryManagers is waiting for to create new Recovery Data.
*
* @return secret types
* @hide
@@ -284,9 +291,9 @@
}
public void recoverySecretAvailable(
- @NonNull KeyStoreRecoveryMetadata recoverySecret) throws RemoteException {
+ @NonNull KeychainProtectionParams recoverySecret) throws RemoteException {
int uid = Binder.getCallingUid();
- if (recoverySecret.getLockScreenUiFormat() == KeyStoreRecoveryMetadata.TYPE_LOCKSCREEN) {
+ if (recoverySecret.getLockScreenUiFormat() == KeychainProtectionParams.TYPE_LOCKSCREEN) {
throw new SecurityException(
"Caller " + uid + " is not allowed to set lock screen secret");
}
@@ -312,16 +319,14 @@
@NonNull byte[] verifierPublicKey,
@NonNull byte[] vaultParams,
@NonNull byte[] vaultChallenge,
- @NonNull List<KeyStoreRecoveryMetadata> secrets)
+ @NonNull List<KeychainProtectionParams> secrets)
throws RemoteException {
checkRecoverKeyStorePermission();
int uid = Binder.getCallingUid();
if (secrets.size() != 1) {
- // TODO: support multiple secrets
- throw new ServiceSpecificException(
- ERROR_NOT_YET_SUPPORTED,
- "Only a single KeyStoreRecoveryMetadata is supported");
+ throw new UnsupportedOperationException(
+ "Only a single KeychainProtectionParams is supported");
}
PublicKey publicKey;
@@ -331,13 +336,13 @@
// Should never happen
throw new RuntimeException(e);
} catch (InvalidKeySpecException e) {
- throw new ServiceSpecificException(ERROR_BAD_X509_CERTIFICATE, "Not a valid X509 key");
+ throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT, "Not a valid X509 key");
}
// The raw public key bytes contained in vaultParams must match the ones given in
// verifierPublicKey; otherwise, the user secret may be decrypted by a key that is not owned
// by the original recovery service.
if (!publicKeysMatch(publicKey, vaultParams)) {
- throw new ServiceSpecificException(ERROR_BAD_X509_CERTIFICATE,
+ throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT,
"The public keys given in verifierPublicKey and vaultParams do not match.");
}
@@ -357,9 +362,9 @@
keyClaimant);
} catch (NoSuchAlgorithmException e) {
Log.wtf(TAG, "SecureBox algorithm missing. AOSP must support this.", e);
- throw new ServiceSpecificException(ERROR_UNEXPECTED_MISSING_ALGORITHM, e.getMessage());
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
} catch (InvalidKeyException e) {
- throw new ServiceSpecificException(ERROR_BAD_X509_CERTIFICATE, e.getMessage());
+ throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT, e.getMessage());
}
}
@@ -379,13 +384,13 @@
public Map<String, byte[]> recoverKeys(
@NonNull String sessionId,
@NonNull byte[] encryptedRecoveryKey,
- @NonNull List<KeyEntryRecoveryData> applicationKeys)
+ @NonNull List<WrappedApplicationKey> applicationKeys)
throws RemoteException {
checkRecoverKeyStorePermission();
int uid = Binder.getCallingUid();
RecoverySessionStorage.Entry sessionEntry = mRecoverySessionStorage.get(uid, sessionId);
if (sessionEntry == null) {
- throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR,
+ throw new ServiceSpecificException(ERROR_SESSION_EXPIRED,
String.format(Locale.US,
"Application uid=%d does not have pending session '%s'", uid, sessionId));
}
@@ -418,17 +423,15 @@
// Impossible: all algorithms must be supported by AOSP
throw new RuntimeException(e);
} catch (KeyStoreException | UnrecoverableKeyException e) {
- throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR, e.getMessage());
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
} catch (InsecureUserException e) {
throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage());
}
try {
return mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias);
- } catch (KeyStoreException | InvalidKeyException e) {
- throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR, e.getMessage());
- } catch (RecoverableKeyStorageException e) {
- throw new ServiceSpecificException(ERROR_DATABASE_ERROR, e.getMessage());
+ } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) {
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
}
}
@@ -452,12 +455,12 @@
encryptedClaimResponse);
return KeySyncUtils.decryptRecoveryKey(sessionEntry.getLskfHash(), locallyEncryptedKey);
} catch (InvalidKeyException | AEADBadTagException e) {
- throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR,
+ throw new ServiceSpecificException(ERROR_DECRYPTION_FAILED,
"Failed to decrypt recovery key " + e.getMessage());
} catch (NoSuchAlgorithmException e) {
// Should never happen: all the algorithms used are required by AOSP implementations
- throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR, e.getMessage());
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
}
}
@@ -469,9 +472,9 @@
*/
private Map<String, byte[]> recoverApplicationKeys(
@NonNull byte[] recoveryKey,
- @NonNull List<KeyEntryRecoveryData> applicationKeys) throws RemoteException {
+ @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException {
HashMap<String, byte[]> keyMaterialByAlias = new HashMap<>();
- for (KeyEntryRecoveryData applicationKey : applicationKeys) {
+ for (WrappedApplicationKey applicationKey : applicationKeys) {
String alias = applicationKey.getAlias();
byte[] encryptedKeyMaterial = applicationKey.getEncryptedKeyMaterial();
@@ -482,7 +485,7 @@
} catch (NoSuchAlgorithmException e) {
Log.wtf(TAG, "Missing SecureBox algorithm. AOSP required to support this.", e);
throw new ServiceSpecificException(
- ERROR_UNEXPECTED_MISSING_ALGORITHM, e.getMessage());
+ ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
} catch (InvalidKeyException | AEADBadTagException e) {
throw new ServiceSpecificException(ERROR_DECRYPTION_FAILED,
"Failed to recover key with alias '" + alias + "': " + e.getMessage());
@@ -555,7 +558,7 @@
private void checkRecoverKeyStorePermission() {
mContext.enforceCallingOrSelfPermission(
- RecoverableKeyStoreLoader.PERMISSION_RECOVER_KEYSTORE,
+ Manifest.permission.RECOVER_KEYSTORE,
"Caller " + Binder.getCallingUid() + " doesn't have RecoverKeyStore permission.");
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
index 54aa9f0..0042e10 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
@@ -17,7 +17,7 @@
package com.android.server.locksettings.recoverablekeystore;
import android.util.Log;
-import android.security.recoverablekeystore.RecoverableKeyStoreLoader;
+import android.security.keystore.RecoveryManager;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
@@ -97,7 +97,7 @@
/*nonce=*/ cipher.getIV(),
/*keyMaterial=*/ encryptedKeyMaterial,
/*platformKeyGenerationId=*/ wrappingKey.getGenerationId(),
- RecoverableKeyStoreLoader.RECOVERY_STATUS_SYNC_IN_PROGRESS);
+ RecoveryManager.RECOVERY_STATUS_SYNC_IN_PROGRESS);
}
/**
@@ -107,14 +107,14 @@
* @param keyMaterial The encrypted bytes of the key material.
* @param platformKeyGenerationId The generation ID of the key used to wrap this key.
*
- * @see RecoverableKeyStoreLoader.RECOVERY_STATUS_SYNC_IN_PROGRESS
+ * @see RecoveryManager.RECOVERY_STATUS_SYNC_IN_PROGRESS
* @hide
*/
public WrappedKey(byte[] nonce, byte[] keyMaterial, int platformKeyGenerationId) {
mNonce = nonce;
mKeyMaterial = keyMaterial;
mPlatformKeyGenerationId = platformKeyGenerationId;
- mRecoveryStatus = RecoverableKeyStoreLoader.RECOVERY_STATUS_SYNC_IN_PROGRESS;
+ mRecoveryStatus = RecoveryManager.RECOVERY_STATUS_SYNC_IN_PROGRESS;
}
/**
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
index c6f3ede..2b1416e 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
@@ -336,17 +336,8 @@
* @hide
*/
public long setRecoveryServicePublicKey(int userId, int uid, PublicKey publicKey) {
- SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
- ContentValues values = new ContentValues();
- values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY, publicKey.getEncoded());
- String selection =
- RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " = ? AND "
- + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + " = ?";
- String[] selectionArguments = {Integer.toString(userId), Integer.toString(uid)};
-
- ensureRecoveryServiceMetadataEntryExists(userId, uid);
- return db.update(
- RecoveryServiceMetadataEntry.TABLE_NAME, values, selection, selectionArguments);
+ return setBytes(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY,
+ publicKey.getEncoded());
}
/**
@@ -393,63 +384,27 @@
*/
@Nullable
public PublicKey getRecoveryServicePublicKey(int userId, int uid) {
- SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
-
- String[] projection = {
- RecoveryServiceMetadataEntry._ID,
- RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID,
- RecoveryServiceMetadataEntry.COLUMN_NAME_UID,
- RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY};
- String selection =
- RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " = ? AND "
- + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + " = ?";
- String[] selectionArguments = {Integer.toString(userId), Integer.toString(uid)};
-
- try (
- Cursor cursor = db.query(
- RecoveryServiceMetadataEntry.TABLE_NAME,
- projection,
- selection,
- selectionArguments,
- /*groupBy=*/ null,
- /*having=*/ null,
- /*orderBy=*/ null)
- ) {
- int count = cursor.getCount();
- if (count == 0) {
- return null;
- }
- if (count > 1) {
- Log.wtf(TAG,
- String.format(Locale.US,
- "%d PublicKey entries found for userId=%d uid=%d. "
- + "Should only ever be 0 or 1.", count, userId, uid));
- return null;
- }
- cursor.moveToFirst();
- int idx = cursor.getColumnIndexOrThrow(
- RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY);
- if (cursor.isNull(idx)) {
- return null;
- }
- byte[] keyBytes = cursor.getBlob(idx);
- try {
- return decodeX509Key(keyBytes);
- } catch (InvalidKeySpecException e) {
- Log.wtf(TAG,
- String.format(Locale.US,
- "Recovery service public key entry cannot be decoded for "
- + "userId=%d uid=%d.",
- userId, uid));
- return null;
- }
+ byte[] keyBytes =
+ getBytes(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY);
+ if (keyBytes == null) {
+ return null;
+ }
+ try {
+ return decodeX509Key(keyBytes);
+ } catch (InvalidKeySpecException e) {
+ Log.wtf(TAG,
+ String.format(Locale.US,
+ "Recovery service public key entry cannot be decoded for "
+ + "userId=%d uid=%d.",
+ userId, uid));
+ return null;
}
}
/**
* Updates the list of user secret types used for end-to-end encryption.
* If no secret types are set, recovery snapshot will not be created.
- * See {@code KeyStoreRecoveryMetadata}
+ * See {@code KeychainProtectionParams}
*
* @param userId The userId of the profile the application is running under.
* @param uid The uid of the application.
@@ -617,14 +572,14 @@
*
* @param userId The userId of the profile the application is running under.
* @param uid The uid of the application.
- * @param serverParameters The server parameters.
+ * @param serverParams The server parameters.
* @return The primary key of the inserted row, or -1 if failed.
*
* @hide
*/
- public long setServerParameters(int userId, int uid, long serverParameters) {
- return setLong(userId, uid,
- RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMETERS, serverParameters);
+ public long setServerParams(int userId, int uid, byte[] serverParams) {
+ return setBytes(userId, uid,
+ RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMS, serverParams);
}
/**
@@ -638,9 +593,8 @@
* @hide
*/
@Nullable
- public Long getServerParameters(int userId, int uid) {
- return getLong(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMETERS);
-
+ public byte[] getServerParams(int userId, int uid) {
+ return getBytes(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMS);
}
/**
@@ -704,6 +658,7 @@
return res != null && res != 0L;
}
+
/**
* Returns given long value from the database.
*
@@ -785,6 +740,86 @@
}
/**
+ * Returns given binary value from the database.
+ *
+ * @param userId The userId of the profile the application is running under.
+ * @param uid The uid of the application who initialized the local recovery components.
+ * @param key from {@code RecoveryServiceMetadataEntry}
+ * @return The value that were previously set, or null if there's none.
+ *
+ * @hide
+ */
+ private byte[] getBytes(int userId, int uid, String key) {
+ SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
+
+ String[] projection = {
+ RecoveryServiceMetadataEntry._ID,
+ RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID,
+ RecoveryServiceMetadataEntry.COLUMN_NAME_UID,
+ key};
+ String selection =
+ RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " = ? AND "
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + " = ?";
+ String[] selectionArguments = {Integer.toString(userId), Integer.toString(uid)};
+
+ try (
+ Cursor cursor = db.query(
+ RecoveryServiceMetadataEntry.TABLE_NAME,
+ projection,
+ selection,
+ selectionArguments,
+ /*groupBy=*/ null,
+ /*having=*/ null,
+ /*orderBy=*/ null)
+ ) {
+ int count = cursor.getCount();
+ if (count == 0) {
+ return null;
+ }
+ if (count > 1) {
+ Log.wtf(TAG,
+ String.format(Locale.US,
+ "%d entries found for userId=%d uid=%d. "
+ + "Should only ever be 0 or 1.", count, userId, uid));
+ return null;
+ }
+ cursor.moveToFirst();
+ int idx = cursor.getColumnIndexOrThrow(key);
+ if (cursor.isNull(idx)) {
+ return null;
+ } else {
+ return cursor.getBlob(idx);
+ }
+ }
+ }
+
+ /**
+ * Sets a binary value in the database.
+ *
+ * @param userId The userId of the profile the application is running under.
+ * @param uid The uid of the application who initialized the local recovery components.
+ * @param key defined in {@code RecoveryServiceMetadataEntry}
+ * @param value new value.
+ * @return The primary key of the inserted row, or -1 if failed.
+ *
+ * @hide
+ */
+
+ private long setBytes(int userId, int uid, String key, byte[] value) {
+ SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(key, value);
+ String selection =
+ RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " = ? AND "
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + " = ?";
+ String[] selectionArguments = {Integer.toString(userId), Integer.toString(uid)};
+
+ ensureRecoveryServiceMetadataEntryExists(userId, uid);
+ return db.update(
+ RecoveryServiceMetadataEntry.TABLE_NAME, values, selection, selectionArguments);
+ }
+
+ /**
* Creates an empty row in the recovery service metadata table if such a row doesn't exist for
* the given userId and uid, so db.update will succeed.
*/
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
index 597ae4c..4ee282b 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
@@ -64,7 +64,7 @@
static final String COLUMN_NAME_LAST_SYNCED_AT = "last_synced_at";
/**
- * Status of the key sync {@code RecoverableKeyStoreLoader#setRecoveryStatus}
+ * Status of the key sync {@code RecoveryManager#setRecoveryStatus}
*/
static final String COLUMN_NAME_RECOVERY_STATUS = "recovery_status";
}
@@ -130,6 +130,6 @@
/**
* The server parameters of the recovery service.
*/
- static final String COLUMN_NAME_SERVER_PARAMETERS = "server_parameters";
+ static final String COLUMN_NAME_SERVER_PARAMS = "server_params";
}
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
index 6eb47ee..d96671c 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
@@ -61,7 +61,7 @@
+ RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY + " BLOB,"
+ RecoveryServiceMetadataEntry.COLUMN_NAME_SECRET_TYPES + " TEXT,"
+ RecoveryServiceMetadataEntry.COLUMN_NAME_COUNTER_ID + " INTEGER,"
- + RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMETERS + " INTEGER,"
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMS + " BLOB,"
+ "UNIQUE("
+ RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + ","
+ RecoveryServiceMetadataEntry.COLUMN_NAME_UID + "))";
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
index 011b374..62bb41e 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
@@ -17,7 +17,7 @@
package com.android.server.locksettings.recoverablekeystore.storage;
import android.annotation.Nullable;
-import android.security.recoverablekeystore.KeyStoreRecoveryData;
+import android.security.keystore.KeychainSnapshot;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
@@ -34,12 +34,12 @@
*/
public class RecoverySnapshotStorage {
@GuardedBy("this")
- private final SparseArray<KeyStoreRecoveryData> mSnapshotByUid = new SparseArray<>();
+ private final SparseArray<KeychainSnapshot> mSnapshotByUid = new SparseArray<>();
/**
* Sets the latest {@code snapshot} for the recovery agent {@code uid}.
*/
- public synchronized void put(int uid, KeyStoreRecoveryData snapshot) {
+ public synchronized void put(int uid, KeychainSnapshot snapshot) {
mSnapshotByUid.put(uid, snapshot);
}
@@ -47,7 +47,7 @@
* Returns the latest snapshot for the recovery agent {@code uid}, or null if none exists.
*/
@Nullable
- public synchronized KeyStoreRecoveryData get(int uid) {
+ public synchronized KeychainSnapshot get(int uid) {
return mSnapshotByUid.get(uid);
}
diff --git a/services/core/java/com/android/server/media/MediaUpdateService.java b/services/core/java/com/android/server/media/MediaUpdateService.java
new file mode 100644
index 0000000..016d062
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaUpdateService.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import android.content.Context;
+import android.content.Intent;
+import android.media.IMediaResourceMonitor;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+import android.util.Slog;
+import com.android.server.SystemService;
+
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.IBinder;
+import android.os.PatternMatcher;
+import android.os.ServiceManager;
+import android.media.IMediaExtractorUpdateService;
+
+import java.lang.Exception;
+
+/** This class provides a system service that manages media framework updates. */
+public class MediaUpdateService extends SystemService {
+ private static final String TAG = "MediaUpdateService";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final String MEDIA_UPDATE_PACKAGE_NAME = "com.android.media.update";
+ private static final String EXTRACTOR_UPDATE_SERVICE_NAME = "media.extractor.update";
+
+ private IMediaExtractorUpdateService mMediaExtractorUpdateService;
+
+ public MediaUpdateService(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ // TODO: Uncomment below once sepolicy change is landed.
+ /*
+ if ("userdebug".equals(android.os.Build.TYPE) || "eng".equals(android.os.Build.TYPE)) {
+ connect();
+ registerBroadcastReceiver();
+ }
+ */
+ }
+
+ private void connect() {
+ IBinder binder = ServiceManager.getService(EXTRACTOR_UPDATE_SERVICE_NAME);
+ if (binder != null) {
+ try {
+ binder.linkToDeath(new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ Slog.w(TAG, "mediaextractor died; reconnecting");
+ mMediaExtractorUpdateService = null;
+ connect();
+ }
+ }, 0);
+ } catch (Exception e) {
+ binder = null;
+ }
+ }
+ if (binder != null) {
+ mMediaExtractorUpdateService = IMediaExtractorUpdateService.Stub.asInterface(binder);
+ packageStateChanged();
+ } else {
+ Slog.w(TAG, EXTRACTOR_UPDATE_SERVICE_NAME + " not found.");
+ }
+ }
+
+ private void registerBroadcastReceiver() {
+ BroadcastReceiver updateReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_SYSTEM)
+ != UserHandle.USER_SYSTEM) {
+ // Ignore broadcast for non system users. We don't want to update system
+ // service multiple times.
+ return;
+ }
+ switch (intent.getAction()) {
+ case Intent.ACTION_PACKAGE_REMOVED:
+ if (intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) {
+ // The existing package is updated. Will be handled with the
+ // following ACTION_PACKAGE_ADDED case.
+ return;
+ }
+ packageStateChanged();
+ break;
+ case Intent.ACTION_PACKAGE_CHANGED:
+ packageStateChanged();
+ break;
+ case Intent.ACTION_PACKAGE_ADDED:
+ packageStateChanged();
+ break;
+ }
+ }
+ };
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addDataScheme("package");
+ filter.addDataSchemeSpecificPart(MEDIA_UPDATE_PACKAGE_NAME, PatternMatcher.PATTERN_LITERAL);
+
+ getContext().registerReceiverAsUser(updateReceiver, UserHandle.ALL, filter,
+ null /* broadcast permission */, null /* handler */);
+ }
+
+ private void packageStateChanged() {
+ ApplicationInfo packageInfo = null;
+ boolean pluginsAvailable = false;
+ try {
+ packageInfo = getContext().getPackageManager().getApplicationInfo(
+ MEDIA_UPDATE_PACKAGE_NAME, PackageManager.MATCH_SYSTEM_ONLY);
+ pluginsAvailable = packageInfo.enabled;
+ } catch (Exception e) {
+ Slog.v(TAG, "package '" + MEDIA_UPDATE_PACKAGE_NAME + "' not installed");
+ }
+ loadExtractorPlugins(
+ (packageInfo != null && pluginsAvailable) ? packageInfo.sourceDir : "");
+ }
+
+ private void loadExtractorPlugins(String apkPath) {
+ try {
+ if (mMediaExtractorUpdateService != null) {
+ mMediaExtractorUpdateService.loadPlugins(apkPath);
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Error in loadPlugins", e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index da71049..ff9b2fd 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -2793,6 +2793,11 @@
handleNetworkPoliciesUpdateAL(true);
}
}
+
+ final Intent intent = new Intent(SubscriptionManager.ACTION_SUBSCRIPTION_PLANS_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
+ mContext.sendBroadcast(intent, android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/net/watchlist/OWNERS b/services/core/java/com/android/server/net/watchlist/OWNERS
new file mode 100644
index 0000000..a3d4b85
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/OWNERS
@@ -0,0 +1,3 @@
+rickywai@google.com
+alanstokes@google.com
+simonjw@google.com
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 37e6ae9..42093e8 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -71,6 +71,7 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@@ -98,6 +99,9 @@
static final String ATT_APPROVED_LIST = "approved";
static final String ATT_USER_ID = "user";
static final String ATT_IS_PRIMARY = "primary";
+ static final String ATT_VERSION = "version";
+
+ static final int DB_VERSION = 1;
static final int APPROVAL_BY_PACKAGE = 0;
static final int APPROVAL_BY_COMPONENT = 1;
@@ -295,6 +299,8 @@
public void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
out.startTag(null, getConfig().xmlTag);
+ out.attribute(null, ATT_VERSION, String.valueOf(DB_VERSION));
+
if (forBackup) {
trimApprovedListsAccordingToInstalledServices();
}
@@ -336,6 +342,14 @@
public void readXml(XmlPullParser parser)
throws XmlPullParserException, IOException {
+ // upgrade xml
+ int xmlVersion = XmlUtils.readIntAttribute(parser, ATT_VERSION, 0);
+ final List<UserInfo> activeUsers = mUm.getUsers(true);
+ for (UserInfo userInfo : activeUsers) {
+ upgradeXml(xmlVersion, userInfo.getUserHandle().getIdentifier());
+ }
+
+ // read grants
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
String tag = parser.getName();
@@ -346,6 +360,7 @@
if (type == XmlPullParser.START_TAG) {
if (TAG_MANAGED_SERVICES.equals(tag)) {
Slog.i(TAG, "Read " + mConfig.caption + " permissions from xml");
+
final String approved = XmlUtils.readStringAttribute(parser, ATT_APPROVED_LIST);
final int userId = XmlUtils.readIntAttribute(parser, ATT_USER_ID, 0);
final boolean isPrimary =
@@ -360,6 +375,8 @@
rebindServices(false);
}
+ protected void upgradeXml(final int xmlVersion, final int userId) {}
+
private void loadAllowedComponentsFromSettings() {
UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
@@ -379,7 +396,7 @@
Slog.d(TAG, "Done loading approved values from settings");
}
- private void addApprovedList(String approved, int userId, boolean isPrimary) {
+ protected void addApprovedList(String approved, int userId, boolean isPrimary) {
if (TextUtils.isEmpty(approved)) {
approved = "";
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 575c44d..2f6618f 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -434,6 +434,7 @@
}
}
}
+
String defaultDndAccess = getContext().getResources().getString(
com.android.internal.R.string.config_defaultDndAccessPackages);
if (defaultListenerAccess != null) {
@@ -446,6 +447,29 @@
}
}
}
+
+ readDefaultAssistant(userId);
+ }
+
+ protected void readDefaultAssistant(int userId) {
+ String defaultAssistantAccess = getContext().getResources().getString(
+ com.android.internal.R.string.config_defaultAssistantAccessPackage);
+ if (defaultAssistantAccess != null) {
+ // Gather all notification assistant components for candidate pkg. There should
+ // only be one
+ Set<ComponentName> approvedAssistants =
+ mAssistants.queryPackageForServices(defaultAssistantAccess,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+ for (ComponentName cn : approvedAssistants) {
+ try {
+ getBinderService().setNotificationAssistantAccessGrantedForUser(cn,
+ userId, true);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ }
}
void readPolicyXml(InputStream stream, boolean forRestore)
@@ -1155,6 +1179,7 @@
}
}
+ @VisibleForTesting
void clearNotifications() {
mEnqueuedNotifications.clear();
mNotificationList.clear();
@@ -1374,7 +1399,8 @@
AppGlobals.getPackageManager(), getContext().getPackageManager(),
getLocalService(LightsManager.class),
new NotificationListeners(AppGlobals.getPackageManager()),
- new NotificationAssistants(AppGlobals.getPackageManager()),
+ new NotificationAssistants(getContext(), mNotificationLock, mUserProfiles,
+ AppGlobals.getPackageManager()),
new ConditionProviders(getContext(), mUserProfiles, AppGlobals.getPackageManager()),
null, snoozeHelper, new NotificationUsageStats(getContext()),
new AtomicFile(new File(systemDir, "notification_policy.xml")),
@@ -2917,12 +2943,34 @@
}
}
+ /**
+ * Sets the notification policy. Apps that target API levels below
+ * {@link android.os.Build.VERSION_CODES#P} cannot make DND silence
+ * {@link Policy#PRIORITY_CATEGORY_ALARMS} or
+ * {@link Policy#PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER}
+ */
@Override
public void setNotificationPolicy(String pkg, Policy policy) {
enforcePolicyAccess(pkg, "setNotificationPolicy");
final long identity = Binder.clearCallingIdentity();
try {
+ final ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(pkg,
+ 0, UserHandle.getUserId(MY_UID));
+
+ if (applicationInfo.targetSdkVersion <= Build.VERSION_CODES.O_MR1) {
+ Policy currPolicy = mZenModeHelper.getNotificationPolicy();
+
+ int priorityCategories = policy.priorityCategories
+ | (currPolicy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS)
+ | (currPolicy.priorityCategories &
+ Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER);
+ policy = new Policy(priorityCategories,
+ policy.priorityCallSenders, policy.priorityMessageSenders,
+ policy.suppressedVisualEffects);
+ }
+
mZenModeHelper.setNotificationPolicy(policy);
+ } catch (RemoteException e) {
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -5531,8 +5579,9 @@
public class NotificationAssistants extends ManagedServices {
static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants";
- public NotificationAssistants(IPackageManager pm) {
- super(getContext(), mNotificationLock, mUserProfiles, pm);
+ public NotificationAssistants(Context context, Object lock, UserProfiles up,
+ IPackageManager pm) {
+ super(context, lock, up, pm);
}
@Override
@@ -5637,6 +5686,14 @@
public boolean isEnabled() {
return !getServices().isEmpty();
}
+
+ protected void upgradeXml(final int xmlVersion, final int userId) {
+ if (xmlVersion == 0) {
+ // one time approval of the OOB assistant
+ Slog.d(TAG, "Approving default notification assistant for user " + userId);
+ readDefaultAssistant(userId);
+ }
+ }
}
public class NotificationListeners extends ManagedServices {
@@ -6050,7 +6107,7 @@
public static final String USAGE = "help\n"
+ "allow_listener COMPONENT [user_id]\n"
+ "disallow_listener COMPONENT [user_id]\n"
- + "set_assistant COMPONENT\n"
+ + "allow_assistant COMPONENT\n"
+ "remove_assistant COMPONENT\n"
+ "allow_dnd PACKAGE\n"
+ "disallow_dnd PACKAGE";
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index a30e0639..fd9ffb2 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -278,7 +278,7 @@
// VisibleForTesting
public static String[] getExtraPeople(Bundle extras) {
- Object people = extras.get(Notification.EXTRA_PEOPLE);
+ Object people = extras.get(Notification.EXTRA_PEOPLE_LIST);
if (people instanceof String[]) {
return (String[]) people;
}
@@ -305,6 +305,16 @@
return array;
}
+ if (arrayList.get(0) instanceof Notification.Person) {
+ ArrayList<Notification.Person> list = (ArrayList<Notification.Person>) arrayList;
+ final int N = list.size();
+ String[] array = new String[N];
+ for (int i = 0; i < N; i++) {
+ array[i] = list.get(i).resolveToLegacyUri();
+ }
+ return array;
+ }
+
return null;
}
@@ -459,7 +469,9 @@
lookupResult = searchContacts(mContext, uri);
} else {
lookupResult = new LookupResult(); // invalid person for the cache
- Slog.w(TAG, "unsupported URI " + handle);
+ if (!"name".equals(uri.getScheme())) {
+ Slog.w(TAG, "unsupported URI " + handle);
+ }
}
if (lookupResult != null) {
synchronized (mPeopleCache) {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 700ccad..b5d2844 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -822,21 +822,24 @@
@VisibleForTesting
protected void applyRestrictions() {
- final boolean zen = mZenMode != Global.ZEN_MODE_OFF;
+ final boolean zenPriorityOnly = mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ final boolean zenSilence = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
+ final boolean zenAlarmsOnly = mZenMode == Global.ZEN_MODE_ALARMS;
// notification restrictions
final boolean muteNotifications =
(mSuppressedEffects & SUPPRESSED_EFFECT_NOTIFICATIONS) != 0;
// call restrictions
- final boolean muteCalls = zen && !mConfig.allowCalls && !mConfig.allowRepeatCallers
+ final boolean muteCalls = zenAlarmsOnly
+ || (zenPriorityOnly && !mConfig.allowCalls && !mConfig.allowRepeatCallers)
|| (mSuppressedEffects & SUPPRESSED_EFFECT_CALLS) != 0;
// alarm restrictions
- final boolean muteAlarms = zen && !mConfig.allowAlarms;
+ final boolean muteAlarms = zenPriorityOnly && !mConfig.allowAlarms;
// alarm restrictions
- final boolean muteMediaAndSystemSounds = zen && !mConfig.allowMediaSystemOther;
+ final boolean muteMediaAndSystemSounds = zenPriorityOnly && !mConfig.allowMediaSystemOther;
// total silence restrictions
- final boolean muteEverything = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS
- || areAllBehaviorSoundsMuted();
+ final boolean muteEverything = zenSilence
+ || (zenPriorityOnly && areAllBehaviorSoundsMuted());
for (int usage : AudioAttributes.SDK_USAGES) {
final int suppressionBehavior = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index be66fe2..4b3758d 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -281,13 +281,14 @@
public void dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet,
int dexoptNeeded, @Nullable String outputPath, int dexFlags,
String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries,
- @Nullable String seInfo, boolean downgrade)
+ @Nullable String seInfo, boolean downgrade, int targetSdkVersion)
throws InstallerException {
assertValidInstructionSet(instructionSet);
if (!checkBeforeRemote()) return;
try {
mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
- dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade);
+ dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade,
+ targetSdkVersion);
} catch (Exception e) {
throw InstallerException.from(e);
}
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index c964f91..af20cd7 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -49,7 +49,6 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;
-import com.android.server.pm.permission.BasePermission;
import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -302,7 +301,7 @@
// into account but also allow the value from the old computation to avoid
// data loss.
final String[] signaturesSha256Digests = PackageUtils.computeSignaturesSha256Digests(
- pkg.mSignatures);
+ pkg.mSigningDetails.signatures);
final String signaturesSha256Digest = PackageUtils.computeSignaturesSha256Digest(
signaturesSha256Digests);
@@ -313,7 +312,7 @@
}
// For backwards compatibility we accept match based on first signature
- if (pkg.mSignatures.length > 1 && currentCookieFile.equals(computeInstantCookieFile(
+ if (pkg.mSigningDetails.signatures.length > 1 && currentCookieFile.equals(computeInstantCookieFile(
pkg.packageName, signaturesSha256Digests[0], userId))) {
return;
}
@@ -1176,12 +1175,13 @@
// We prefer the modern computation procedure where all certs are taken
// into account and delete the file derived via the legacy hash computation.
File newCookieFile = computeInstantCookieFile(pkg.packageName,
- PackageUtils.computeSignaturesSha256Digest(pkg.mSignatures), userId);
- if (pkg.mSignatures.length > 0) {
- File oldCookieFile = peekInstantCookieFile(pkg.packageName, userId);
- if (oldCookieFile != null && !newCookieFile.equals(oldCookieFile)) {
- oldCookieFile.delete();
- }
+ PackageUtils.computeSignaturesSha256Digest(pkg.mSigningDetails.signatures), userId);
+ if (!pkg.mSigningDetails.hasSignatures()) {
+ Slog.wtf(LOG_TAG, "Parsed Instant App contains no valid signatures!");
+ }
+ File oldCookieFile = peekInstantCookieFile(pkg.packageName, userId);
+ if (oldCookieFile != null && !newCookieFile.equals(oldCookieFile)) {
+ oldCookieFile.delete();
}
cancelPendingPersistLPw(pkg, userId);
addPendingPersistCookieLPw(userId, pkg, cookie, newCookieFile);
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index fca9585..93d3b77 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -188,7 +188,7 @@
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Passed invalid package to keyset validation.");
}
- ArraySet<PublicKey> signingKeys = pkg.mSigningKeys;
+ ArraySet<PublicKey> signingKeys = pkg.mSigningDetails.publicKeys;
if (signingKeys == null || !(signingKeys.size() > 0) || signingKeys.contains(null)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Package has invalid signing-key-set.");
@@ -223,7 +223,7 @@
PackageSetting ps = mPackages.get(pkg.packageName);
Preconditions.checkNotNull(ps, "pkg: " + pkg.packageName
+ "does not have a corresponding entry in mPackages.");
- addSigningKeySetToPackageLPw(ps, pkg.mSigningKeys);
+ addSigningKeySetToPackageLPw(ps, pkg.mSigningDetails.publicKeys);
if (pkg.mKeySetMapping != null) {
addDefinedKeySetsToPackageLPw(ps, pkg.mKeySetMapping);
if (pkg.mUpgradeKeySets != null) {
@@ -371,7 +371,7 @@
long[] upgradeKeySets = oldPS.keySetData.getUpgradeKeySets();
for (int i = 0; i < upgradeKeySets.length; i++) {
Set<PublicKey> upgradeSet = getPublicKeysFromKeySetLPr(upgradeKeySets[i]);
- if (upgradeSet != null && newPkg.mSigningKeys.containsAll(upgradeSet)) {
+ if (upgradeSet != null && newPkg.mSigningDetails.publicKeys.containsAll(upgradeSet)) {
return true;
}
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index b06b583..1717b3d 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -652,6 +652,7 @@
activityInfo.name.equals(component.getClassName())) {
// Found an activity with category launcher that matches
// this component so ok to launch.
+ launchIntent.setPackage(null);
launchIntent.setComponent(component);
mContext.startActivityAsUser(launchIntent, opts, user);
return;
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 03f662a..0395011 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -260,12 +260,13 @@
public void dexopt(String apkPath, int uid, @Nullable String pkgName,
String instructionSet, int dexoptNeeded, @Nullable String outputPath,
int dexFlags, String compilerFilter, @Nullable String volumeUuid,
- @Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade)
+ @Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade,
+ int targetSdkVersion)
throws InstallerException {
final StringBuilder builder = new StringBuilder();
- // The version. Right now it's 3.
- builder.append("3 ");
+ // The version. Right now it's 4.
+ builder.append("4 ");
builder.append("dexopt");
@@ -281,6 +282,7 @@
encodeParameter(builder, sharedLibraries);
encodeParameter(builder, seInfo);
encodeParameter(builder, downgrade);
+ encodeParameter(builder, targetSdkVersion);
commands.add(builder.toString());
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 730a9fd..91df87b 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -274,7 +274,7 @@
// primary dex files.
mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo,
- false /* downgrade*/);
+ false /* downgrade*/, pkg.applicationInfo.targetSdkVersion);
if (packageStats != null) {
long endTime = System.currentTimeMillis();
@@ -395,7 +395,7 @@
mInstaller.dexopt(path, info.uid, info.packageName, isa, /*dexoptNeeded*/ 0,
/*oatDir*/ null, dexoptFlags,
compilerFilter, info.volumeUuid, classLoaderContext, info.seInfoUser,
- options.isDowngrade());
+ options.isDowngrade(), info.targetSdkVersion);
}
return DEX_OPT_PERFORMED;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5577de8..a43818a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -58,7 +58,6 @@
import android.content.pm.PackageParser.ApkLite;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.Signature;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Binder;
@@ -84,6 +83,7 @@
import android.util.ExceptionUtils;
import android.util.MathUtils;
import android.util.Slog;
+import android.util.apk.ApkSignatureVerifier;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.NativeLibraryHelper;
@@ -107,7 +107,7 @@
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -227,9 +227,7 @@
@GuardedBy("mLock")
private long mVersionCode;
@GuardedBy("mLock")
- private Signature[] mSignatures;
- @GuardedBy("mLock")
- private Certificate[][] mCertificates;
+ private PackageParser.SigningDetails mSigningDetails;
/**
* Path to the validated base APK for this session, which may point at an
@@ -857,7 +855,7 @@
}
Preconditions.checkNotNull(mPackageName);
- Preconditions.checkNotNull(mSignatures);
+ Preconditions.checkNotNull(mSigningDetails);
Preconditions.checkNotNull(mResolvedBaseFile);
if (needToAskForPermissionsLocked()) {
@@ -938,7 +936,7 @@
mRelinquished = true;
mPm.installStage(mPackageName, stageDir, localObserver, params,
- mInstallerPackageName, mInstallerUid, user, mCertificates);
+ mInstallerPackageName, mInstallerUid, user, mSigningDetails);
}
/**
@@ -957,7 +955,7 @@
throws PackageManagerException {
mPackageName = null;
mVersionCode = -1;
- mSignatures = null;
+ mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
mResolvedBaseFile = null;
mResolvedStagedFiles.clear();
@@ -990,11 +988,8 @@
for (File addedFile : addedFiles) {
final ApkLite apk;
try {
- int flags = PackageParser.PARSE_COLLECT_CERTIFICATES;
- if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
- flags |= PackageParser.PARSE_IS_EPHEMERAL;
- }
- apk = PackageParser.parseApkLite(addedFile, flags);
+ apk = PackageParser.parseApkLite(
+ addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
}
@@ -1009,9 +1004,8 @@
mPackageName = apk.packageName;
mVersionCode = apk.getLongVersionCode();
}
- if (mSignatures == null) {
- mSignatures = apk.signatures;
- mCertificates = apk.certificates;
+ if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
+ mSigningDetails = apk.signingDetails;
}
assertApkConsistentLocked(String.valueOf(addedFile), apk);
@@ -1060,8 +1054,15 @@
mPackageName = pkgInfo.packageName;
mVersionCode = pkgInfo.getLongVersionCode();
}
- if (mSignatures == null) {
- mSignatures = pkgInfo.signatures;
+ if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
+ try {
+ mSigningDetails = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts(
+ pkgInfo.applicationInfo.sourceDir,
+ PackageParser.SigningDetails.SignatureSchemeVersion.JAR);
+ } catch (PackageParserException e) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Couldn't obtain signatures from base APK");
+ }
}
}
@@ -1155,7 +1156,7 @@
+ " version code " + apk.versionCode + " inconsistent with "
+ mVersionCode);
}
- if (!Signature.areExactMatch(mSignatures, apk.signatures)) {
+ if (!mSigningDetails.signaturesMatchExactly(apk.signingDetails)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
tag + " signatures are inconsistent");
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 44aad44..711ea13 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import static android.Manifest.permission.DELETE_PACKAGES;
+import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
import static android.Manifest.permission.INSTALL_PACKAGES;
import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
@@ -167,7 +168,6 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
-import android.content.pm.PackageManager.PackageInfoFlags;
import android.content.pm.PackageManagerInternal.PackageListObserver;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.ActivityIntentInfo;
@@ -176,6 +176,7 @@
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageParser.ParseFlags;
import android.content.pm.PackageParser.ServiceIntentInfo;
+import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.PackageStats;
import android.content.pm.PackageUserState;
import android.content.pm.ParceledListSlice;
@@ -337,7 +338,6 @@
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SecureRandom;
-import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -2697,6 +2697,12 @@
mSettings.getDisabledSystemPkgLPr(ps.name);
if (disabledPs.codePath == null || !disabledPs.codePath.exists()
|| disabledPs.pkg == null) {
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "Possibly deleted app: " + ps.dumpState_temp()
+ + "; path: " + (disabledPs.codePath == null ? "<<NULL>>":disabledPs.codePath)
+ + "; pkg: " + (disabledPs.pkg==null?"<<NULL>>":disabledPs.pkg.toString()));
+}
possiblyDeletedUpdatedSystemApps.add(ps.name);
}
}
@@ -2748,7 +2754,10 @@
for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
mSettings.removeDisabledSystemPackageLPw(deletedAppName);
-
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "remove update; name: " + deletedAppName + ", exists? " + (deletedPkg != null));
+}
final String msg;
if (deletedPkg == null) {
// should have found an update, but, we didn't; remove everything
@@ -5360,7 +5369,7 @@
|| filterAppAccessLPr(ps2, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- return compareSignatures(p1.mSignatures, p2.mSignatures);
+ return compareSignatures(p1.mSigningDetails.signatures, p2.mSigningDetails.signatures);
}
}
@@ -8184,36 +8193,36 @@
}
private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg,
- final @ParseFlags int parseFlags) throws PackageManagerException {
+ final @ParseFlags int parseFlags, boolean forceCollect) throws PackageManagerException {
// When upgrading from pre-N MR1, verify the package time stamp using the package
// directory and not the APK file.
final long lastModifiedTime = mIsPreNMR1Upgrade
? new File(pkg.codePath).lastModified() : getLastModifiedTime(pkg);
- if (ps != null
+ if (ps != null && !forceCollect
&& ps.codePathString.equals(pkg.codePath)
&& ps.timeStamp == lastModifiedTime
&& !isCompatSignatureUpdateNeeded(pkg)
&& !isRecoverSignatureUpdateNeeded(pkg)) {
- long mSigningKeySetId = ps.keySetData.getProperSigningKeySet();
- final KeySetManagerService ksms = mSettings.mKeySetManagerService;
- ArraySet<PublicKey> signingKs;
- synchronized (mPackages) {
- signingKs = ksms.getPublicKeysFromKeySetLPr(mSigningKeySetId);
- }
if (ps.signatures.mSignatures != null
&& ps.signatures.mSignatures.length != 0
- && signingKs != null) {
- // Optimization: reuse the existing cached certificates
+ && ps.signatures.mSignatureSchemeVersion != SignatureSchemeVersion.UNKNOWN) {
+ // Optimization: reuse the existing cached signing data
// if the package appears to be unchanged.
- pkg.mSignatures = ps.signatures.mSignatures;
- pkg.mSigningKeys = signingKs;
- return;
+ try {
+ pkg.mSigningDetails = new PackageParser.SigningDetails(ps.signatures.mSignatures,
+ ps.signatures.mSignatureSchemeVersion);
+ return;
+ } catch (CertificateException e) {
+ Slog.e(TAG, "Attempt to read public keys from persisted signatures failed for "
+ + ps.name, e);
+ }
}
Slog.w(TAG, "PackageSetting for " + ps.name
+ " is missing signatures. Collecting certs again to recover them.");
} else {
- Slog.i(TAG, toString() + " changed; collecting certs");
+ Slog.i(TAG, pkg.codePath + " changed; collecting certs" +
+ (forceCollect ? " (forced)" : ""));
}
try {
@@ -8293,14 +8302,14 @@
}
// Scan the parent
- PackageParser.Package scannedPkg = scanPackageInternalLI(pkg, parseFlags,
+ PackageParser.Package scannedPkg = addForInitLI(pkg, parseFlags,
scanFlags, currentTime, user);
// Scan the children
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPackage = pkg.childPackages.get(i);
- scanPackageInternalLI(childPackage, parseFlags, scanFlags,
+ addForInitLI(childPackage, parseFlags, scanFlags,
currentTime, user);
}
@@ -8312,313 +8321,308 @@
return scannedPkg;
}
+ // Temporary to catch potential issues with refactoring
+ private static boolean REFACTOR_DEBUG = true;
/**
- * Scans a package and returns the newly parsed package.
- * @throws PackageManagerException on a parse error.
+ * Adds a new package to the internal data structures during platform initialization.
+ * <p>After adding, the package is known to the system and available for querying.
+ * <p>For packages located on the device ROM [eg. packages located in /system, /vendor,
+ * etc...], additional checks are performed. Basic verification [such as ensuring
+ * matching signatures, checking version codes, etc...] occurs if the package is
+ * identical to a previously known package. If the package fails a signature check,
+ * the version installed on /data will be removed. If the version of the new package
+ * is less than or equal than the version on /data, it will be ignored.
+ * <p>Regardless of the package location, the results are applied to the internal
+ * structures and the package is made available to the rest of the system.
+ * <p>NOTE: The return value should be removed. It's the passed in package object.
*/
- private PackageParser.Package scanPackageInternalLI(PackageParser.Package pkg,
+ private PackageParser.Package addForInitLI(PackageParser.Package pkg,
@ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user)
throws PackageManagerException {
- PackageSetting ps = null;
- PackageSetting updatedPs;
- // reader
- synchronized (mPackages) {
- // Look to see if we already know about this package.
- String oldName = mSettings.getRenamedPackageLPr(pkg.packageName);
- if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) {
- // This package has been renamed to its original name. Let's
- // use that.
- ps = mSettings.getPackageLPr(oldName);
- }
- // If there was no original package, see one for the real package name.
- if (ps == null) {
- ps = mSettings.getPackageLPr(pkg.packageName);
- }
- // Check to see if this package could be hiding/updating a system
- // package. Must look for it either under the original or real
- // package name depending on our state.
- updatedPs = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName);
- if (DEBUG_INSTALL && updatedPs != null) Slog.d(TAG, "updatedPkg = " + updatedPs);
+ final boolean scanSystemPartition = (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0;
+ final String renamedPkgName;
+ final PackageSetting disabledPkgSetting;
+ final boolean isSystemPkgUpdated;
+ final boolean pkgAlreadyExists;
+ PackageSetting pkgSetting;
- // If this is a package we don't know about on the system partition, we
- // may need to remove disabled child packages on the system partition
- // or may need to not add child packages if the parent apk is updated
- // on the data partition and no longer defines this child package.
- if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
- // If this is a parent package for an updated system app and this system
- // app got an OTA update which no longer defines some of the child packages
- // we have to prune them from the disabled system packages.
- PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName);
- if (disabledPs != null) {
- final int scannedChildCount = (pkg.childPackages != null)
- ? pkg.childPackages.size() : 0;
- final int disabledChildCount = disabledPs.childPackageNames != null
- ? disabledPs.childPackageNames.size() : 0;
- for (int i = 0; i < disabledChildCount; i++) {
- String disabledChildPackageName = disabledPs.childPackageNames.get(i);
- boolean disabledPackageAvailable = false;
- for (int j = 0; j < scannedChildCount; j++) {
- PackageParser.Package childPkg = pkg.childPackages.get(j);
- if (childPkg.packageName.equals(disabledChildPackageName)) {
- disabledPackageAvailable = true;
- break;
- }
- }
- if (!disabledPackageAvailable) {
- mSettings.removeDisabledSystemPackageLPw(disabledChildPackageName);
- }
- }
- }
- }
- }
-
- final boolean isUpdatedPkg = updatedPs != null;
- final boolean isUpdatedSystemPkg = isUpdatedPkg && (scanFlags & SCAN_AS_SYSTEM) != 0;
- boolean isUpdatedPkgBetter = false;
- // First check if this is a system package that may involve an update
- if (isUpdatedSystemPkg) {
- // If new package is not located in "/system/priv-app" (e.g. due to an OTA),
- // it needs to drop FLAG_PRIVILEGED.
- if (locationIsPrivileged(pkg.codePath)) {
- updatedPs.pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
- } else {
- updatedPs.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
- }
- // If new package is not located in "/oem" (e.g. due to an OTA),
- // it needs to drop FLAG_OEM.
- if (locationIsOem(pkg.codePath)) {
- updatedPs.pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_OEM;
- } else {
- updatedPs.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_OEM;
- }
- // If new package is not located in "/vendor" (e.g. due to an OTA),
- // it needs to drop FLAG_VENDOR.
- if (locationIsVendor(pkg.codePath)) {
- updatedPs.pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_VENDOR;
- } else {
- updatedPs.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_VENDOR;
- }
-
- if (ps != null && !ps.codePathString.equals(pkg.codePath)) {
- // The path has changed from what was last scanned... check the
- // version of the new path against what we have stored to determine
- // what to do.
- if (DEBUG_INSTALL) Slog.d(TAG, "Path changing from " + ps.codePath);
- if (pkg.getLongVersionCode() <= ps.versionCode) {
- // The system package has been updated and the code path does not match
- // Ignore entry. Skip it.
- if (DEBUG_INSTALL) Slog.i(TAG, "Package " + ps.name + " at " + pkg.codePath
- + " ignored: updated version " + ps.versionCode
- + " better than this " + pkg.getLongVersionCode());
- if (!updatedPs.codePathString.equals(pkg.codePath)) {
- Slog.w(PackageManagerService.TAG, "Code path for hidden system pkg "
- + ps.name + " changing from " + updatedPs.codePathString
- + " to " + pkg.codePath);
- final File codePath = new File(pkg.codePath);
- updatedPs.codePath = codePath;
- updatedPs.codePathString = pkg.codePath;
- updatedPs.resourcePath = codePath;
- updatedPs.resourcePathString = pkg.codePath;
- }
- updatedPs.pkg = pkg;
- updatedPs.versionCode = pkg.getLongVersionCode();
-
- // Update the disabled system child packages to point to the package too.
- final int childCount = updatedPs.childPackageNames != null
- ? updatedPs.childPackageNames.size() : 0;
- for (int i = 0; i < childCount; i++) {
- String childPackageName = updatedPs.childPackageNames.get(i);
- PackageSetting updatedChildPkg = mSettings.getDisabledSystemPkgLPr(
- childPackageName);
- if (updatedChildPkg != null) {
- updatedChildPkg.pkg = pkg;
- updatedChildPkg.versionCode = pkg.getLongVersionCode();
- }
- }
- } else {
- // The current app on the system partition is better than
- // what we have updated to on the data partition; switch
- // back to the system partition version.
- // At this point, its safely assumed that package installation for
- // apps in system partition will go through. If not there won't be a working
- // version of the app
- // writer
- synchronized (mPackages) {
- // Just remove the loaded entries from package lists.
- mPackages.remove(ps.name);
- }
-
- logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + pkg.codePath
- + " reverting from " + ps.codePathString
- + ": new version " + pkg.getLongVersionCode()
- + " better than installed " + ps.versionCode);
-
- InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
- ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
- synchronized (mInstallLock) {
- args.cleanUpResourcesLI();
- }
- synchronized (mPackages) {
- mSettings.enableSystemPackageLPw(ps.name);
- }
- isUpdatedPkgBetter = true;
- }
- }
- }
-
- String resourcePath = null;
- String baseResourcePath = null;
- if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !isUpdatedPkgBetter) {
- if (ps != null && ps.resourcePathString != null) {
- resourcePath = ps.resourcePathString;
- baseResourcePath = ps.resourcePathString;
- } else {
- // Should not happen at all. Just log an error.
- Slog.e(TAG, "Resource path not set for package " + pkg.packageName);
- }
- } else {
- resourcePath = pkg.codePath;
- baseResourcePath = pkg.baseCodePath;
- }
-
- // Set application objects path explicitly.
+ // NOTE: installPackageLI() has the same code to setup the package's
+ // application info. This probably should be done lower in the call
+ // stack [such as scanPackageOnly()]. However, we verify the application
+ // info prior to that [in scanPackageNew()] and thus have to setup
+ // the application info early.
pkg.setApplicationVolumeUuid(pkg.volumeUuid);
pkg.setApplicationInfoCodePath(pkg.codePath);
pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
- pkg.setApplicationInfoResourcePath(resourcePath);
- pkg.setApplicationInfoBaseResourcePath(baseResourcePath);
+ pkg.setApplicationInfoResourcePath(pkg.codePath);
+ pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath);
pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
- // throw an exception if we have an update to a system application, but, it's not more
- // recent than the package we've already scanned
- if (isUpdatedSystemPkg && !isUpdatedPkgBetter) {
- // Set CPU Abis to application info.
- if ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0) {
- final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, updatedPs);
- derivePackageAbi(pkg, cpuAbiOverride, false);
- } else {
- pkg.applicationInfo.primaryCpuAbi = updatedPs.primaryCpuAbiString;
- pkg.applicationInfo.secondaryCpuAbi = updatedPs.secondaryCpuAbiString;
+ synchronized (mPackages) {
+ renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage);
+ final String realPkgName = getRealPackageName(pkg, renamedPkgName);
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "Add pkg: " + pkg.packageName + (realPkgName==null?"":", realName: " + realPkgName));
+}
+ if (realPkgName != null) {
+ ensurePackageRenamed(pkg, renamedPkgName);
}
- pkg.mExtras = updatedPs;
+ final PackageSetting originalPkgSetting = getOriginalPackageLocked(pkg, renamedPkgName);
+ final PackageSetting installedPkgSetting = mSettings.getPackageLPr(pkg.packageName);
+ pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
+ pkgAlreadyExists = pkgSetting != null;
+ final String disabledPkgName = pkgAlreadyExists ? pkgSetting.name : pkg.packageName;
+ disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(disabledPkgName);
+ isSystemPkgUpdated = disabledPkgSetting != null;
+ if (DEBUG_INSTALL && isSystemPkgUpdated) {
+ Slog.d(TAG, "updatedPkg = " + disabledPkgSetting);
+ }
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "SSP? " + scanSystemPartition
+ + ", exists? " + pkgAlreadyExists + (pkgAlreadyExists?" "+pkgSetting.toString():"")
+ + ", upgraded? " + isSystemPkgUpdated + (isSystemPkgUpdated?" "+disabledPkgSetting.toString():""));
+}
+
+ final SharedUserSetting sharedUserSetting = (pkg.mSharedUserId != null)
+ ? mSettings.getSharedUserLPw(pkg.mSharedUserId,
+ 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
+ : null;
+ if (DEBUG_PACKAGE_SCANNING
+ && (parseFlags & PackageParser.PARSE_CHATTY) != 0
+ && sharedUserSetting != null) {
+ Log.d(TAG, "Shared UserID " + pkg.mSharedUserId
+ + " (uid=" + sharedUserSetting.userId + "):"
+ + " packages=" + sharedUserSetting.packages);
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "Shared UserID " + pkg.mSharedUserId
+ + " (uid=" + sharedUserSetting.userId + "):"
+ + " packages=" + sharedUserSetting.packages);
+}
+ }
+
+ if (scanSystemPartition) {
+ // Potentially prune child packages. If the application on the /system
+ // partition has been updated via OTA, but, is still disabled by a
+ // version on /data, cycle through all of its children packages and
+ // remove children that are no longer defined.
+ if (isSystemPkgUpdated) {
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "Disable child packages");
+}
+ final int scannedChildCount = (pkg.childPackages != null)
+ ? pkg.childPackages.size() : 0;
+ final int disabledChildCount = disabledPkgSetting.childPackageNames != null
+ ? disabledPkgSetting.childPackageNames.size() : 0;
+ for (int i = 0; i < disabledChildCount; i++) {
+ String disabledChildPackageName =
+ disabledPkgSetting.childPackageNames.get(i);
+ boolean disabledPackageAvailable = false;
+ for (int j = 0; j < scannedChildCount; j++) {
+ PackageParser.Package childPkg = pkg.childPackages.get(j);
+ if (childPkg.packageName.equals(disabledChildPackageName)) {
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "Ignore " + disabledChildPackageName);
+}
+ disabledPackageAvailable = true;
+ break;
+ }
+ }
+ if (!disabledPackageAvailable) {
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "Disable " + disabledChildPackageName);
+}
+ mSettings.removeDisabledSystemPackageLPw(disabledChildPackageName);
+ }
+ }
+ // we're updating the disabled package, so, scan it as the package setting
+ final ScanRequest request = new ScanRequest(pkg, sharedUserSetting,
+ disabledPkgSetting /* pkgSetting */, null /* disabledPkgSetting */,
+ null /* originalPkgSetting */, null, parseFlags, scanFlags,
+ (pkg == mPlatformPackage), user);
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "Scan disabled system package");
+Slog.e("TODD",
+ "Pre: " + request.pkgSetting.dumpState_temp());
+}
+final ScanResult result =
+ scanPackageOnlyLI(request, mFactoryTest, -1L);
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "Post: " + (result.success?result.pkgSetting.dumpState_temp():"FAILED scan"));
+}
+ }
+ }
+ }
+
+ final boolean newPkgChangedPaths =
+ pkgAlreadyExists && !pkgSetting.codePathString.equals(pkg.codePath);
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "paths changed? " + newPkgChangedPaths
+ + "; old: " + pkg.codePath
+ + ", new: " + (pkgSetting==null?"<<NULL>>":pkgSetting.codePathString));
+}
+ final boolean newPkgVersionGreater =
+ pkgAlreadyExists && pkg.getLongVersionCode() > pkgSetting.versionCode;
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "version greater? " + newPkgVersionGreater
+ + "; old: " + pkg.getLongVersionCode()
+ + ", new: " + (pkgSetting==null?"<<NULL>>":pkgSetting.versionCode));
+}
+ final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
+ && newPkgChangedPaths && newPkgVersionGreater;
+if (REFACTOR_DEBUG) {
+ Slog.e("TODD",
+ "system better? " + isSystemPkgBetter);
+}
+ if (isSystemPkgBetter) {
+ // The version of the application on /system is greater than the version on
+ // /data. Switch back to the application on /system.
+ // It's safe to assume the application on /system will correctly scan. If not,
+ // there won't be a working copy of the application.
+ synchronized (mPackages) {
+ // just remove the loaded entries from package lists
+ mPackages.remove(pkgSetting.name);
+ }
+
+ logCriticalInfo(Log.WARN,
+ "System package updated;"
+ + " name: " + pkgSetting.name
+ + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
+ + "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "System package changed;"
+ + " name: " + pkgSetting.name
+ + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
+ + "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
+}
+
+ final InstallArgs args = createInstallArgsForExisting(
+ packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
+ pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
+ args.cleanUpResourcesLI();
+ synchronized (mPackages) {
+ mSettings.enableSystemPackageLPw(pkgSetting.name);
+ }
+ }
+
+ if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "THROW exception; system pkg version not good enough");
+}
+ // The version of the application on the /system partition is less than or
+ // equal to the version on the /data partition. Throw an exception and use
+ // the application already installed on the /data partition.
throw new PackageManagerException(Log.WARN, "Package " + pkg.packageName + " at "
- + pkg.codePath + " ignored: updated version " + updatedPs.versionCode
+ + pkg.codePath + " ignored: updated version " + disabledPkgSetting.versionCode
+ " better than this " + pkg.getLongVersionCode());
}
- if (isUpdatedPkg) {
- // updated system applications don't initially have the SCAN_AS_SYSTEM flag set
- scanFlags |= SCAN_AS_SYSTEM;
+ // Verify certificates against what was last scanned. If it is an updated priv app, we will
+ // force the verification. Full apk verification will happen unless apk verity is set up for
+ // the file. In that case, only small part of the apk is verified upfront.
+ collectCertificatesLI(pkgSetting, pkg, parseFlags,
+ PackageManagerServiceUtils.isApkVerificationForced(disabledPkgSetting));
- // An updated privileged application will not have the PARSE_IS_PRIVILEGED
- // flag set initially
- if ((updatedPs.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
- scanFlags |= SCAN_AS_PRIVILEGED;
- }
-
- // An updated OEM app will not have the SCAN_AS_OEM
- // flag set initially
- if ((updatedPs.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
- scanFlags |= SCAN_AS_OEM;
- }
-
- // An updated vendor app will not have the SCAN_AS_VENDOR
- // flag set initially
- if ((updatedPs.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) {
- scanFlags |= SCAN_AS_VENDOR;
- }
- }
-
- // Verify certificates against what was last scanned
- collectCertificatesLI(ps, pkg, parseFlags);
-
- /*
- * A new system app appeared, but we already had a non-system one of the
- * same name installed earlier.
- */
boolean shouldHideSystemApp = false;
- if (!isUpdatedPkg && ps != null
- && (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0 && !isSystemApp(ps)) {
- /*
- * Check to make sure the signatures match first. If they don't,
- * wipe the installed application and its data.
- */
- if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures)
+ // A new application appeared on /system, but, we already have a copy of
+ // the application installed on /data.
+ if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
+ && !pkgSetting.isSystem()) {
+ // if the signatures don't match, wipe the installed application and its data
+ if (compareSignatures(pkgSetting.signatures.mSignatures, pkg.mSigningDetails.signatures)
!= PackageManager.SIGNATURE_MATCH) {
- logCriticalInfo(Log.WARN, "Package " + ps.name + " appeared on system, but"
- + " signatures don't match existing userdata copy; removing");
+ logCriticalInfo(Log.WARN,
+ "System package signature mismatch;"
+ + " name: " + pkgSetting.name);
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "System package signature mismatch;"
+ + " name: " + pkgSetting.name);
+}
try (PackageFreezer freezer = freezePackage(pkg.packageName,
"scanPackageInternalLI")) {
deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null);
}
- ps = null;
- } else {
- /*
- * If the newly-added system app is an older version than the
- * already installed version, hide it. It will be scanned later
- * and re-added like an update.
- */
- if (pkg.getLongVersionCode() <= ps.versionCode) {
- shouldHideSystemApp = true;
- logCriticalInfo(Log.INFO, "Package " + ps.name + " appeared at " + pkg.codePath
- + " but new version " + pkg.getLongVersionCode()
- + " better than installed " + ps.versionCode + "; hiding system");
- } else {
- /*
- * The newly found system app is a newer version that the
- * one previously installed. Simply remove the
- * already-installed application and replace it with our own
- * while keeping the application data.
- */
- logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + pkg.codePath
- + " reverting from " + ps.codePathString + ": new version "
- + pkg.getLongVersionCode() + " better than installed "
- + ps.versionCode);
- InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
- ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
- synchronized (mInstallLock) {
- args.cleanUpResourcesLI();
- }
+ pkgSetting = null;
+ } else if (newPkgVersionGreater) {
+ // The application on /system is newer than the application on /data.
+ // Simply remove the application on /data [keeping application data]
+ // and replace it with the version on /system.
+ logCriticalInfo(Log.WARN,
+ "System package enabled;"
+ + " name: " + pkgSetting.name
+ + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
+ + "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "System package enabled;"
+ + " name: " + pkgSetting.name
+ + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
+ + "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
+}
+ InstallArgs args = createInstallArgsForExisting(
+ packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
+ pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
+ synchronized (mInstallLock) {
+ args.cleanUpResourcesLI();
}
+ } else {
+ // The application on /system is older than the application on /data. Hide
+ // the application on /system and the version on /data will be scanned later
+ // and re-added like an update.
+ shouldHideSystemApp = true;
+ logCriticalInfo(Log.INFO,
+ "System package disabled;"
+ + " name: " + pkgSetting.name
+ + "; old: " + pkgSetting.codePathString + " @ " + pkgSetting.versionCode
+ + "; new: " + pkg.codePath + " @ " + pkg.codePath);
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "System package disabled;"
+ + " name: " + pkgSetting.name
+ + "; old: " + pkgSetting.codePathString + " @ " + pkgSetting.versionCode
+ + "; new: " + pkg.codePath + " @ " + pkg.codePath);
+}
}
}
- // The apk is forward locked (not public) if its code and resources
- // are kept in different files. (except for app in either system or
- // vendor path).
- // TODO grab this value from PackageSettings
- if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
- if (ps != null && !ps.codePath.equals(ps.resourcePath)) {
- parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
- }
- }
-
- final int userId = ((user == null) ? 0 : user.getIdentifier());
- if (ps != null && ps.getInstantApp(userId)) {
- scanFlags |= SCAN_AS_INSTANT_APP;
- }
- if (ps != null && ps.getVirtulalPreload(userId)) {
- scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
- }
-
- // Note that we invoke the following method only if we are about to unpack an application
- PackageParser.Package scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "Scan package");
+Slog.e("TODD",
+ "Pre: " + (pkgSetting==null?"<<NONE>>":pkgSetting.dumpState_temp()));
+}
+ final PackageParser.Package scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user);
+if (REFACTOR_DEBUG) {
+pkgSetting = mSettings.getPackageLPr(pkg.packageName);
+Slog.e("TODD",
+ "Post: " + (pkgSetting==null?"<<NONE>>":pkgSetting.dumpState_temp()));
+}
- /*
- * If the system app should be overridden by a previously installed
- * data, hide the system app now and let the /data/app scan pick it up
- * again.
- */
if (shouldHideSystemApp) {
+if (REFACTOR_DEBUG) {
+Slog.e("TODD",
+ "Disable package: " + pkg.packageName);
+}
synchronized (mPackages) {
mSettings.disableSystemPackageLPw(pkg.packageName, true);
}
}
-
return scannedPkg;
}
@@ -9518,9 +9522,10 @@
final String[] expectedCertDigests = requiredCertDigests[i];
// For apps targeting O MR1 we require explicit enumeration of all certs.
final String[] libCertDigests = (targetSdk > Build.VERSION_CODES.O)
- ? PackageUtils.computeSignaturesSha256Digests(libPkg.mSignatures)
+ ? PackageUtils.computeSignaturesSha256Digests(
+ libPkg.mSigningDetails.signatures)
: PackageUtils.computeSignaturesSha256Digests(
- new Signature[]{libPkg.mSignatures[0]});
+ new Signature[]{libPkg.mSigningDetails.signatures[0]});
// Take a shortcut if sizes don't match. Note that if an app doesn't
// target O we don't parse the "additional-certificate" tags similarly
@@ -9717,9 +9722,52 @@
}
}
+ /**
+ * Returns the actual scan flags depending upon the state of the other settings.
+ * <p>Updated system applications will not have the following flags set
+ * by default and need to be adjusted after the fact:
+ * <ul>
+ * <li>{@link #SCAN_AS_SYSTEM}</li>
+ * <li>{@link #SCAN_AS_PRIVILEGED}</li>
+ * <li>{@link #SCAN_AS_OEM}</li>
+ * <li>{@link #SCAN_AS_VENDOR}</li>
+ * <li>{@link #SCAN_AS_INSTANT_APP}</li>
+ * <li>{@link #SCAN_AS_VIRTUAL_PRELOAD}</li>
+ * </ul>
+ */
+ private static @ScanFlags int adjustScanFlags(@ScanFlags int scanFlags,
+ PackageSetting pkgSetting, PackageSetting disabledPkgSetting, UserHandle user) {
+ if (disabledPkgSetting != null) {
+ // updated system application, must at least have SCAN_AS_SYSTEM
+ scanFlags |= SCAN_AS_SYSTEM;
+ if ((disabledPkgSetting.pkgPrivateFlags
+ & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
+ scanFlags |= SCAN_AS_PRIVILEGED;
+ }
+ if ((disabledPkgSetting.pkgPrivateFlags
+ & ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
+ scanFlags |= SCAN_AS_OEM;
+ }
+ if ((disabledPkgSetting.pkgPrivateFlags
+ & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) {
+ scanFlags |= SCAN_AS_VENDOR;
+ }
+ }
+ if (pkgSetting != null) {
+ final int userId = ((user == null) ? 0 : user.getIdentifier());
+ if (pkgSetting.getInstantApp(userId)) {
+ scanFlags |= SCAN_AS_INSTANT_APP;
+ }
+ if (pkgSetting.getVirtulalPreload(userId)) {
+ scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
+ }
+ }
+ return scanFlags;
+ }
+
@GuardedBy("mInstallLock")
private PackageParser.Package scanPackageNewLI(@NonNull PackageParser.Package pkg,
- final @ParseFlags int parseFlags, final @ScanFlags int scanFlags, long currentTime,
+ final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user) throws PackageManagerException {
final String renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage);
@@ -9737,6 +9785,7 @@
+ " was transferred to another, but its .apk remains");
}
+ scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user);
synchronized (mPackages) {
applyPolicy(pkg, parseFlags, scanFlags);
assertPackageIsValid(pkg, parseFlags, scanFlags);
@@ -9791,6 +9840,7 @@
final @ScanFlags int scanFlags = request.scanFlags;
final PackageSetting oldPkgSetting = request.oldPkgSetting;
final PackageSetting originalPkgSetting = request.originalPkgSetting;
+ final PackageSetting disabledPkgSetting = request.disabledPkgSetting;
final UserHandle user = request.user;
final String realPkgName = request.realPkgName;
final PackageSetting pkgSetting = result.pkgSetting;
@@ -9856,14 +9906,14 @@
if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {
// We just determined the app is signed correctly, so bring
// over the latest parsed certs.
- pkgSetting.signatures.mSignatures = pkg.mSignatures;
+ pkgSetting.signatures.mSignatures = pkg.mSigningDetails.signatures;
} else {
if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"Package " + pkg.packageName + " upgrade keys do not match the "
+ "previously installed version");
} else {
- pkgSetting.signatures.mSignatures = pkg.mSignatures;
+ pkgSetting.signatures.mSignatures = pkg.mSigningDetails.signatures;
String msg = "System package " + pkg.packageName
+ " signature changed; retaining data.";
reportSettingsProblem(Log.WARN, msg);
@@ -9873,8 +9923,8 @@
try {
final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg);
final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg);
- final boolean compatMatch = verifySignatures(signatureCheckPs, pkg.mSignatures,
- compareCompat, compareRecover);
+ final boolean compatMatch = verifySignatures(signatureCheckPs, disabledPkgSetting,
+ pkg.mSigningDetails, compareCompat, compareRecover);
// The new KeySets will be re-added later in the scanning process.
if (compatMatch) {
synchronized (mPackages) {
@@ -9883,14 +9933,14 @@
}
// We just determined the app is signed correctly, so bring
// over the latest parsed certs.
- pkgSetting.signatures.mSignatures = pkg.mSignatures;
+ pkgSetting.signatures.mSignatures = pkg.mSigningDetails.signatures;
} catch (PackageManagerException e) {
if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
throw e;
}
// The signature has changed, but this package is in the system
// image... let's recover!
- pkgSetting.signatures.mSignatures = pkg.mSignatures;
+ pkgSetting.signatures.mSignatures = pkg.mSigningDetails.signatures;
// However... if this package is part of a shared user, but it
// doesn't match the signature of the shared user, let's fail.
// What this means is that you can't change the signatures
@@ -9898,7 +9948,7 @@
// that unreasonable.
if (signatureCheckPs.sharedUser != null) {
if (compareSignatures(signatureCheckPs.sharedUser.signatures.mSignatures,
- pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
+ pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH) {
throw new PackageManagerException(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
"Signature mismatch for shared user: "
@@ -9963,10 +10013,16 @@
*/
private static @Nullable String getRealPackageName(@NonNull PackageParser.Package pkg,
@Nullable String renamedPkgName) {
- if (pkg.mOriginalPackages == null || !pkg.mOriginalPackages.contains(renamedPkgName)) {
- return null;
+ if (isPackageRenamed(pkg, renamedPkgName)) {
+ return pkg.mRealPackage;
}
- return pkg.mRealPackage;
+ return null;
+ }
+
+ /** Returns {@code true} if the package has been renamed. Otherwise, {@code false}. */
+ private static boolean isPackageRenamed(@NonNull PackageParser.Package pkg,
+ @Nullable String renamedPkgName) {
+ return pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(renamedPkgName);
}
/**
@@ -9979,7 +10035,7 @@
@GuardedBy("mPackages")
private @Nullable PackageSetting getOriginalPackageLocked(@NonNull PackageParser.Package pkg,
@Nullable String renamedPkgName) {
- if (pkg.mOriginalPackages == null || pkg.mOriginalPackages.contains(renamedPkgName)) {
+ if (!isPackageRenamed(pkg, renamedPkgName)) {
return null;
}
for (int i = pkg.mOriginalPackages.size() - 1; i >= 0; --i) {
@@ -10102,7 +10158,6 @@
usesStaticLibraries = new String[pkg.usesStaticLibraries.size()];
pkg.usesStaticLibraries.toArray(usesStaticLibraries);
}
-
final boolean createNewPackage = (pkgSetting == null);
if (createNewPackage) {
final String parentPackageName = (pkg.parentPackage != null)
@@ -10126,17 +10181,17 @@
// secondaryCpuAbi are not known at this point so we always update them
// to null here, only to reset them at a later point.
Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
- destCodeFile, pkg.applicationInfo.nativeLibraryDir,
+ destCodeFile, destResourceFile, pkg.applicationInfo.nativeLibraryDir,
pkg.applicationInfo.primaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi,
pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags,
pkg.getChildPackageNames(), UserManagerService.getInstance(),
usesStaticLibraries, pkg.usesStaticLibrariesVersions);
}
if (createNewPackage && originalPkgSetting != null) {
- // If we are first transitioning from an original package,
- // fix up the new package's name now. We need to do this after
- // looking up the package under its new name, so getPackageLP
- // can take care of fiddling things correctly.
+ // This is the initial transition from the original package, so,
+ // fix up the new package's name now. We must do this after looking
+ // up the package under its new name, so getPackageLP takes care of
+ // fiddling things correctly.
pkg.setPackageName(originalPkgSetting.name);
// File a report about this.
@@ -10223,7 +10278,7 @@
// would've already compiled the app without taking the package setting into
// account.
if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
- if (cpuAbiOverride == null && pkgSetting.cpuAbiOverrideString != null) {
+ if (cpuAbiOverride == null && pkg.packageName != null) {
Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +
" for package " + pkg.packageName);
}
@@ -10238,7 +10293,7 @@
pkg.cpuAbiOverride = cpuAbiOverride;
if (DEBUG_ABI_SELECTION) {
- Slog.d(TAG, "Resolved nativeLibraryRoot for " + pkg.applicationInfo.packageName
+ Slog.d(TAG, "Resolved nativeLibraryRoot for " + pkg.packageName
+ " to root=" + pkg.applicationInfo.nativeLibraryRootDir + ", isa="
+ pkg.applicationInfo.nativeLibraryRootRequiresIsa);
}
@@ -10293,6 +10348,22 @@
}
pkgSetting.setTimeStamp(scanFileTime);
+ pkgSetting.pkg = pkg;
+ pkgSetting.pkgFlags = pkg.applicationInfo.flags;
+ if (pkg.getLongVersionCode() != pkgSetting.versionCode) {
+ pkgSetting.versionCode = pkg.getLongVersionCode();
+ }
+ // Update volume if needed
+ final String volumeUuid = pkg.applicationInfo.volumeUuid;
+ if (!Objects.equals(volumeUuid, pkgSetting.volumeUuid)) {
+ Slog.i(PackageManagerService.TAG,
+ "Update" + (pkgSetting.isSystem() ? " system" : "")
+ + " package " + pkg.packageName
+ + " volume from " + pkgSetting.volumeUuid
+ + " to " + volumeUuid);
+ pkgSetting.volumeUuid = volumeUuid;
+ }
+
return new ScanResult(true, pkgSetting, changedAbiCodePath);
}
@@ -10693,6 +10764,26 @@
}
}
}
+
+ // Verify that packages sharing a user with a privileged app are marked as privileged.
+ if (!pkg.isPrivileged() && (pkg.mSharedUserId != null)) {
+ SharedUserSetting sharedUserSetting = null;
+ try {
+ sharedUserSetting = mSettings.getSharedUserLPw(pkg.mSharedUserId, 0, 0, false);
+ } catch (PackageManagerException ignore) {}
+ if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
+ // Exempt SharedUsers signed with the platform key.
+ PackageSetting platformPkgSetting = mSettings.mPackages.get("android");
+ if ((platformPkgSetting.signatures.mSignatures != null) &&
+ (compareSignatures(platformPkgSetting.signatures.mSignatures,
+ pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH)) {
+ throw new PackageManagerException("Apps that share a user with a " +
+ "privileged app must themselves be marked as privileged. " +
+ pkg.packageName + " shares privileged user " +
+ pkg.mSharedUserId + ".");
+ }
+ }
+ }
}
}
@@ -13204,7 +13295,7 @@
void installStage(String packageName, File stagedDir,
IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
String installerPackageName, int installerUid, UserHandle user,
- Certificate[][] certificates) {
+ PackageParser.SigningDetails signingDetails) {
if (DEBUG_EPHEMERAL) {
if ((sessionParams.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
Slog.d(TAG, "Ephemeral install of " + packageName);
@@ -13222,7 +13313,7 @@
final InstallParams params = new InstallParams(origin, null, observer,
sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
verificationInfo, user, sessionParams.abiOverride,
- sessionParams.grantedRuntimePermissions, certificates, installReason);
+ sessionParams.grantedRuntimePermissions, signingDetails, installReason);
params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
msg.obj = params;
@@ -13826,7 +13917,7 @@
final PackageParser.Package pkg = mPackages.get(verifierInfo.packageName);
if (pkg == null) {
return -1;
- } else if (pkg.mSignatures.length != 1) {
+ } else if (pkg.mSigningDetails.signatures.length != 1) {
Slog.i(TAG, "Verifier package " + verifierInfo.packageName
+ " has more than one signature; ignoring");
return -1;
@@ -13840,7 +13931,7 @@
final byte[] expectedPublicKey;
try {
- final Signature verifierSig = pkg.mSignatures[0];
+ final Signature verifierSig = pkg.mSigningDetails.signatures[0];
final PublicKey publicKey = verifierSig.getPublicKey();
expectedPublicKey = publicKey.getEncoded();
} catch (CertificateException e) {
@@ -14532,13 +14623,13 @@
final String packageAbiOverride;
final String[] grantedRuntimePermissions;
final VerificationInfo verificationInfo;
- final Certificate[][] certificates;
+ final PackageParser.SigningDetails signingDetails;
final int installReason;
InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, String volumeUuid,
VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,
- String[] grantedPermissions, Certificate[][] certificates, int installReason) {
+ String[] grantedPermissions, PackageParser.SigningDetails signingDetails, int installReason) {
super(user);
this.origin = origin;
this.move = move;
@@ -14549,7 +14640,7 @@
this.verificationInfo = verificationInfo;
this.packageAbiOverride = packageAbiOverride;
this.grantedRuntimePermissions = grantedPermissions;
- this.certificates = certificates;
+ this.signingDetails = signingDetails;
this.installReason = installReason;
}
@@ -14980,7 +15071,7 @@
/** If non-null, drop an async trace when the install completes */
final String traceMethod;
final int traceCookie;
- final Certificate[][] certificates;
+ final PackageParser.SigningDetails signingDetails;
final int installReason;
// The list of instruction sets supported by this app. This is currently
@@ -14992,7 +15083,7 @@
int installFlags, String installerPackageName, String volumeUuid,
UserHandle user, String[] instructionSets,
String abiOverride, String[] installGrantPermissions,
- String traceMethod, int traceCookie, Certificate[][] certificates,
+ String traceMethod, int traceCookie, PackageParser.SigningDetails signingDetails,
int installReason) {
this.origin = origin;
this.move = move;
@@ -15006,7 +15097,7 @@
this.installGrantPermissions = installGrantPermissions;
this.traceMethod = traceMethod;
this.traceCookie = traceCookie;
- this.certificates = certificates;
+ this.signingDetails = signingDetails;
this.installReason = installReason;
}
@@ -15102,7 +15193,7 @@
params.installerPackageName, params.volumeUuid,
params.getUser(), null /*instructionSets*/, params.packageAbiOverride,
params.grantedRuntimePermissions,
- params.traceMethod, params.traceCookie, params.certificates,
+ params.traceMethod, params.traceCookie, params.signingDetails,
params.installReason);
if (isFwdLocked()) {
throw new IllegalArgumentException("Forward locking only supported in ASEC");
@@ -15112,7 +15203,7 @@
/** Existing install */
FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) {
super(OriginInfo.fromNothing(), null, null, 0, null, null, null, instructionSets,
- null, null, null, 0, null /*certificates*/,
+ null, null, null, 0, PackageParser.SigningDetails.UNKNOWN,
PackageManager.INSTALL_REASON_UNKNOWN);
this.codeFile = (codePath != null) ? new File(codePath) : null;
this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
@@ -15333,7 +15424,7 @@
params.installerPackageName, params.volumeUuid,
params.getUser(), null /* instruction sets */, params.packageAbiOverride,
params.grantedRuntimePermissions,
- params.traceMethod, params.traceCookie, params.certificates,
+ params.traceMethod, params.traceCookie, params.signingDetails,
params.installReason);
}
@@ -15672,7 +15763,8 @@
}
} else {
// default to original signature matching
- if (compareSignatures(oldPackage.mSignatures, pkg.mSignatures)
+ if (compareSignatures(oldPackage.mSigningDetails.signatures,
+ pkg.mSigningDetails.signatures)
!= PackageManager.SIGNATURE_MATCH) {
res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"New package has a different signature: " + pkgName);
@@ -16384,7 +16476,6 @@
| PackageParser.PARSE_ENFORCE_CODE
| (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
| (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0)
- | (instantApp ? PackageParser.PARSE_IS_EPHEMERAL : 0)
| (forceSdk ? PackageParser.PARSE_FORCE_SDK : 0);
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
@@ -16412,19 +16503,29 @@
return;
}
- // Instant apps must have target SDK >= O and have targetSanboxVersion >= 2
- if (instantApp && pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) {
- Slog.w(TAG, "Instant app package " + pkg.packageName + " does not target O");
- res.setError(INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE,
- "Instant app package must target O");
- return;
- }
- if (instantApp && pkg.applicationInfo.targetSandboxVersion != 2) {
- Slog.w(TAG, "Instant app package " + pkg.packageName
- + " does not target targetSandboxVersion 2");
- res.setError(INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE,
- "Instant app package must use targetSanboxVersion 2");
- return;
+ // Instant apps have several additional install-time checks.
+ if (instantApp) {
+ if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
+ Slog.w(TAG,
+ "Instant app package " + pkg.packageName + " does not target at least O");
+ res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
+ "Instant app package must target at least O");
+ return;
+ }
+ if (pkg.applicationInfo.targetSandboxVersion != 2) {
+ Slog.w(TAG, "Instant app package " + pkg.packageName
+ + " does not target targetSandboxVersion 2");
+ res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
+ "Instant app package must use targetSandboxVersion 2");
+ return;
+ }
+ if (pkg.mSharedUserId != null) {
+ Slog.w(TAG, "Instant app package " + pkg.packageName
+ + " may not declare sharedUserId.");
+ res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
+ "Instant app package may not declare a sharedUserId");
+ return;
+ }
}
if (pkg.applicationInfo.isStaticSharedLibrary()) {
@@ -16484,14 +16585,8 @@
try {
// either use what we've been given or parse directly from the APK
- if (args.certificates != null) {
- try {
- PackageParser.populateCertificates(pkg, args.certificates);
- } catch (PackageParserException e) {
- // there was something wrong with the certificates we were given;
- // try to pull them from the APK
- PackageParser.collectCertificates(pkg, parseFlags);
- }
+ if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) {
+ pkg.setSigningDetails(args.signingDetails);
} else {
PackageParser.collectCertificates(pkg, parseFlags);
}
@@ -16500,6 +16595,15 @@
return;
}
+ if (instantApp && pkg.mSigningDetails.signatureSchemeVersion
+ < SignatureSchemeVersion.SIGNING_BLOCK_V2) {
+ Slog.w(TAG, "Instant app package " + pkg.packageName
+ + " is not signed with at least APK Signature Scheme v2");
+ res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
+ "Instant app package must be signed with APK Signature Scheme v2 or greater");
+ return;
+ }
+
// Get rid of all references to package scan path via parser.
pp = null;
String oldCodePath = null;
@@ -16609,8 +16713,10 @@
try {
final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg);
final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg);
+ // We don't care about disabledPkgSetting on install for now.
final boolean compatMatch = verifySignatures(
- signatureCheckPs, pkg.mSignatures, compareCompat, compareRecover);
+ signatureCheckPs, null, pkg.mSigningDetails, compareCompat,
+ compareRecover);
// The new KeySets will be re-added later in the scanning process.
if (compatMatch) {
synchronized (mPackages) {
@@ -16661,7 +16767,7 @@
sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, pkg);
} else {
sigsOk = compareSignatures(sourcePackageSetting.signatures.mSignatures,
- pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
+ pkg.mSigningDetails.signatures) == PackageManager.SIGNATURE_MATCH;
}
if (!sigsOk) {
// If the owning package is the system itself, we log but allow
@@ -16937,7 +17043,8 @@
for (ActivityIntentInfo filter : a.intents) {
if (filter.needsVerification() && needsNetworkVerificationLPr(filter)) {
if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.d(TAG, "Intent filter needs verification, so processing all filters");
+ Slog.d(TAG,
+ "Intent filter needs verification, so processing all filters");
}
needToVerify = true;
break;
@@ -18267,7 +18374,8 @@
null /*enabledComponents*/,
null /*disabledComponents*/,
ps.readUserState(nextUserId).domainVerificationStatus,
- 0, PackageManager.INSTALL_REASON_UNKNOWN);
+ 0, PackageManager.INSTALL_REASON_UNKNOWN,
+ null /*harmfulAppWarning*/);
}
mSettings.writeKernelMappingLPr(ps);
}
@@ -22245,8 +22353,8 @@
final OriginInfo origin = OriginInfo.fromExistingFile(codeFile);
final InstallParams params = new InstallParams(origin, move, installObserver, installFlags,
installerPackageName, volumeUuid, null /*verificationInfo*/, user,
- packageAbiOverride, null /*grantedPermissions*/, null /*certificates*/,
- PackageManager.INSTALL_REASON_UNKNOWN);
+ packageAbiOverride, null /*grantedPermissions*/,
+ PackageParser.SigningDetails.UNKNOWN, PackageManager.INSTALL_REASON_UNKNOWN);
params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params));
msg.obj = params;
@@ -23579,6 +23687,47 @@
}
return unusedPackages;
}
+
+ @Override
+ public void setHarmfulAppWarning(@NonNull String packageName, @Nullable CharSequence warning,
+ int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingAppId = UserHandle.getAppId(callingUid);
+
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
+ true /*requireFullPermission*/, true /*checkShell*/, "setHarmfulAppInfo");
+
+ if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID &&
+ checkUidPermission(SET_HARMFUL_APP_WARNINGS, callingUid) != PERMISSION_GRANTED) {
+ throw new SecurityException("Caller must have the "
+ + SET_HARMFUL_APP_WARNINGS + " permission.");
+ }
+
+ synchronized(mPackages) {
+ mSettings.setHarmfulAppWarningLPw(packageName, warning, userId);
+ scheduleWritePackageRestrictionsLocked(userId);
+ }
+ }
+
+ @Nullable
+ @Override
+ public CharSequence getHarmfulAppWarning(@NonNull String packageName, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingAppId = UserHandle.getAppId(callingUid);
+
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
+ true /*requireFullPermission*/, true /*checkShell*/, "getHarmfulAppInfo");
+
+ if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID &&
+ checkUidPermission(SET_HARMFUL_APP_WARNINGS, callingUid) != PERMISSION_GRANTED) {
+ throw new SecurityException("Caller must have the "
+ + SET_HARMFUL_APP_WARNINGS + " permission.");
+ }
+
+ synchronized(mPackages) {
+ return mSettings.getHarmfulAppWarningLPr(packageName, userId);
+ }
+ }
}
interface PackageSender {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 20ec9b5..7b96ca6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -33,10 +33,12 @@
import com.android.server.pm.dex.PackageDexUsage;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AppGlobals;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
import android.os.Build;
@@ -45,6 +47,7 @@
import android.os.FileUtils;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.service.pm.PackageServiceDumpProto;
import android.system.ErrnoException;
@@ -504,13 +507,13 @@
* system upgrade) and {@code scannedSigs} will be in the newer format.
*/
private static boolean matchSignaturesCompat(String packageName,
- PackageSignatures packageSignatures, Signature[] parsedSignatures) {
+ PackageSignatures packageSignatures, PackageParser.SigningDetails parsedSignatures) {
ArraySet<Signature> existingSet = new ArraySet<Signature>();
for (Signature sig : packageSignatures.mSignatures) {
existingSet.add(sig);
}
ArraySet<Signature> scannedCompatSet = new ArraySet<Signature>();
- for (Signature sig : parsedSignatures) {
+ for (Signature sig : parsedSignatures.signatures) {
try {
Signature[] chainSignatures = sig.getChainSignatures();
for (Signature chainSig : chainSignatures) {
@@ -547,27 +550,69 @@
}
/**
+ * Make sure the updated priv app is signed with the same key as the original APK file on the
+ * /system partition.
+ *
+ * <p>The rationale is that {@code disabledPkg} is a PackageSetting backed by xml files in /data
+ * and is not tamperproof.
+ */
+ private static boolean matchSignatureInSystem(PackageSetting pkgSetting,
+ PackageSetting disabledPkgSetting) {
+ try {
+ PackageParser.collectCertificates(disabledPkgSetting.pkg,
+ PackageParser.PARSE_IS_SYSTEM_DIR);
+ if (compareSignatures(pkgSetting.signatures.mSignatures,
+ disabledPkgSetting.signatures.mSignatures)
+ != PackageManager.SIGNATURE_MATCH) {
+ logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " +
+ pkgSetting.name);
+ return false;
+ }
+ } catch (PackageParserException e) {
+ logCriticalInfo(Log.ERROR, "Failed to collect cert for " + pkgSetting.name + ": " +
+ e.getMessage());
+ return false;
+ }
+ return true;
+ }
+
+ /** Returns true to force apk verification if the updated package (in /data) is a priv app. */
+ static boolean isApkVerificationForced(@Nullable PackageSetting disabledPs) {
+ return disabledPs != null && disabledPs.isPrivileged() &&
+ SystemProperties.getInt("ro.apk_verity.mode", 0) != 0;
+ }
+
+ /**
* Verifies that signatures match.
* @returns {@code true} if the compat signatures were matched; otherwise, {@code false}.
* @throws PackageManagerException if the signatures did not match.
*/
public static boolean verifySignatures(PackageSetting pkgSetting,
- Signature[] parsedSignatures, boolean compareCompat, boolean compareRecover)
+ PackageSetting disabledPkgSetting, PackageParser.SigningDetails parsedSignatures,
+ boolean compareCompat, boolean compareRecover)
throws PackageManagerException {
final String packageName = pkgSetting.name;
boolean compatMatch = false;
if (pkgSetting.signatures.mSignatures != null) {
// Already existing package. Make sure signatures match
- boolean match = compareSignatures(pkgSetting.signatures.mSignatures, parsedSignatures)
+ boolean match = compareSignatures(pkgSetting.signatures.mSignatures,
+ parsedSignatures.signatures)
== PackageManager.SIGNATURE_MATCH;
if (!match && compareCompat) {
- match = matchSignaturesCompat(packageName, pkgSetting.signatures, parsedSignatures);
+ match = matchSignaturesCompat(packageName, pkgSetting.signatures,
+ parsedSignatures);
compatMatch = match;
}
if (!match && compareRecover) {
match = matchSignaturesRecover(
- packageName, pkgSetting.signatures.mSignatures, parsedSignatures);
+ packageName, pkgSetting.signatures.mSignatures,
+ parsedSignatures.signatures);
}
+
+ if (!match && isApkVerificationForced(disabledPkgSetting)) {
+ match = matchSignatureInSystem(pkgSetting, disabledPkgSetting);
+ }
+
if (!match) {
throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"Package " + packageName +
@@ -578,14 +623,14 @@
if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) {
// Already existing package. Make sure signatures match
boolean match = compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,
- parsedSignatures) == PackageManager.SIGNATURE_MATCH;
+ parsedSignatures.signatures) == PackageManager.SIGNATURE_MATCH;
if (!match && compareCompat) {
match = matchSignaturesCompat(
packageName, pkgSetting.sharedUser.signatures, parsedSignatures);
}
if (!match && compareRecover) {
- match = matchSignaturesRecover(
- packageName, pkgSetting.sharedUser.signatures.mSignatures, parsedSignatures);
+ match = matchSignaturesRecover(packageName,
+ pkgSetting.sharedUser.signatures.mSignatures, parsedSignatures.signatures);
compatMatch |= match;
}
if (!match) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 2d82c46..bd1cd33 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -230,6 +230,8 @@
return runGetInstantAppResolver();
case "has-feature":
return runHasFeature();
+ case "set-harmful-app-warning":
+ return runSetHarmfulAppWarning();
default: {
String nextArg = getNextArg();
if (nextArg == null) {
@@ -1277,7 +1279,7 @@
return runRemoveSplit(packageName, splitName);
}
- userId = translateUserId(userId, "runUninstall");
+ userId = translateUserId(userId, true /*allowAll*/, "runUninstall");
if (userId == UserHandle.USER_ALL) {
userId = UserHandle.USER_SYSTEM;
flags |= PackageManager.DELETE_ALL_USERS;
@@ -2088,6 +2090,29 @@
return 0;
}
+ private int runSetHarmfulAppWarning() throws RemoteException {
+ int userId = UserHandle.USER_CURRENT;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ } else {
+ getErrPrintWriter().println("Error: Unknown option: " + opt);
+ return -1;
+ }
+ }
+
+ userId = translateUserId(userId, false /*allowAll*/, "runSetHarmfulAppWarning");
+
+ final String packageName = getNextArgRequired();
+ final String warning = getNextArg();
+
+ mInterface.setHarmfulAppWarning(packageName, warning, userId);
+
+ return 0;
+ }
+
private static String checkAbiArgument(String abi) {
if (TextUtils.isEmpty(abi)) {
throw new IllegalArgumentException("Missing ABI argument");
@@ -2107,14 +2132,14 @@
throw new IllegalArgumentException("ABI " + abi + " not supported on this device");
}
- private int translateUserId(int userId, String logContext) {
+ private int translateUserId(int userId, boolean allowAll, String logContext) {
return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
- userId, true, true, logContext, "pm command");
+ userId, allowAll, true, logContext, "pm command");
}
private int doCreateSession(SessionParams params, String installerPackageName, int userId)
throws RemoteException {
- userId = translateUserId(userId, "runInstallCreate");
+ userId = translateUserId(userId, true /*allowAll*/, "runInstallCreate");
if (userId == UserHandle.USER_ALL) {
userId = UserHandle.USER_SYSTEM;
params.installFlags |= PackageManager.INSTALL_ALL_USERS;
@@ -2634,6 +2659,9 @@
pw.println("");
pw.println(" get-instantapp-resolver");
pw.println(" Return the name of the component that is the current instant app installer.");
+ pw.println("");
+ pw.println(" set-harmful-app-warning [--user <USER_ID>] <PACKAGE> [<WARNING>]");
+ pw.println(" Mark the app as harmful with the given warning message.");
pw.println();
Intent.printIntentArgsHelp(pw , "");
}
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 2b91b7d..2a2430c 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -97,6 +97,35 @@
+ " " + name + "/" + appId + "}";
}
+ // Temporary to catch potential issues with refactoring
+ public String dumpState_temp() {
+ String flags = "";
+ flags += ((pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0 ? "U" : "");
+ flags += ((pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0 ? "S" : "");
+ if ("".equals(flags)) {
+ flags = "-";
+ }
+ String privFlags = "";
+ privFlags += ((pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0 ? "P" : "");
+ privFlags += ((pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0 ? "O" : "");
+ privFlags += ((pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0 ? "V" : "");
+ if ("".equals(privFlags)) {
+ privFlags = "-";
+ }
+ return "PackageSetting{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " " + name + (realName == null ? "" : "("+realName+")") + "/" + appId + (sharedUser==null?"":" u:" + sharedUser.name+"("+sharedUserId+")")
+ + ", ver:" + versionCode
+ + ", path: " + codePath
+ + ", pABI: " + primaryCpuAbiString
+ + ", sABI: " + secondaryCpuAbiString
+ + ", oABI: " + cpuAbiOverrideString
+ + ", flags: " + flags
+ + ", privFlags: " + privFlags
+ + ", pkg: " + (pkg == null ? "<<NULL>>" : pkg.dumpState_temp())
+ + "}";
+ }
+
public void copyFrom(PackageSetting orig) {
super.copyFrom(orig);
doCopy(orig);
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 809e16c..e3c4c43 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -437,7 +437,8 @@
boolean notLaunched, boolean hidden, boolean suspended, boolean instantApp,
boolean virtualPreload, String lastDisableAppCaller,
ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
- int domainVerifState, int linkGeneration, int installReason) {
+ int domainVerifState, int linkGeneration, int installReason,
+ String harmfulAppWarning) {
PackageUserState state = modifyUserState(userId);
state.ceDataInode = ceDataInode;
state.enabled = enabled;
@@ -454,6 +455,7 @@
state.installReason = installReason;
state.instantApp = instantApp;
state.virtualPreload = virtualPreload;
+ state.harmfulAppWarning = harmfulAppWarning;
}
ArraySet<String> getEnabledComponents(int userId) {
@@ -620,4 +622,14 @@
proto.end(userToken);
}
}
+
+ void setHarmfulAppWarning(int userId, String harmfulAppWarning) {
+ PackageUserState userState = modifyUserState(userId);
+ userState.harmfulAppWarning = harmfulAppWarning;
+ }
+
+ String getHarmfulAppWarning(int userId) {
+ PackageUserState userState = readUserState(userId);
+ return userState.harmfulAppWarning;
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageSignatures.java b/services/core/java/com/android/server/pm/PackageSignatures.java
index f5c81e4..d567d5c 100644
--- a/services/core/java/com/android/server/pm/PackageSignatures.java
+++ b/services/core/java/com/android/server/pm/PackageSignatures.java
@@ -22,6 +22,8 @@
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.Signature;
import android.util.Log;
@@ -30,15 +32,17 @@
class PackageSignatures {
Signature[] mSignatures;
+ @SignatureSchemeVersion int mSignatureSchemeVersion;
PackageSignatures(PackageSignatures orig) {
if (orig != null && orig.mSignatures != null) {
mSignatures = orig.mSignatures.clone();
+ mSignatureSchemeVersion = orig.mSignatureSchemeVersion;
}
}
- PackageSignatures(Signature[] sigs) {
- assignSignatures(sigs);
+ PackageSignatures(PackageParser.SigningDetails signingDetails) {
+ assignSignatures(signingDetails);
}
PackageSignatures() {
@@ -52,6 +56,7 @@
serializer.startTag(null, tagName);
serializer.attribute(null, "count",
Integer.toString(mSignatures.length));
+ serializer.attribute(null, "schemeVersion", Integer.toString(mSignatureSchemeVersion));
for (int i=0; i<mSignatures.length; i++) {
serializer.startTag(null, "cert");
final Signature sig = mSignatures[i];
@@ -84,6 +89,15 @@
+ " no count at " + parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
}
+ String schemeVersionStr = parser.getAttributeValue(null, "schemeVersion");
+ if (schemeVersionStr == null) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <signatures> has no schemeVersion at "
+ + parser.getPositionDescription());
+ mSignatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
+ } else {
+ mSignatureSchemeVersion = Integer.parseInt(countStr);
+ }
final int count = Integer.parseInt(countStr);
mSignatures = new Signature[count];
int pos = 0;
@@ -174,14 +188,15 @@
}
}
- void assignSignatures(Signature[] sigs) {
- if (sigs == null) {
+ void assignSignatures(PackageParser.SigningDetails signingDetails) {
+ mSignatureSchemeVersion = signingDetails.signatureSchemeVersion;
+ if (!signingDetails.hasSignatures()) {
mSignatures = null;
return;
}
- mSignatures = new Signature[sigs.length];
- for (int i=0; i<sigs.length; i++) {
- mSignatures[i] = sigs[i];
+ mSignatures = new Signature[signingDetails.signatures.length];
+ for (int i=0; i<signingDetails.signatures.length; i++) {
+ mSignatures[i] = signingDetails.signatures[i];
}
}
@@ -190,7 +205,9 @@
StringBuffer buf = new StringBuffer(128);
buf.append("PackageSignatures{");
buf.append(Integer.toHexString(System.identityHashCode(this)));
- buf.append(" [");
+ buf.append(" version:");
+ buf.append(mSignatureSchemeVersion);
+ buf.append(", signatures:[");
if (mSignatures != null) {
for (int i=0; i<mSignatures.length; i++) {
if (i > 0) buf.append(", ");
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index fbf3d82..2552643 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -17,9 +17,8 @@
package com.android.server.pm;
import android.content.pm.PackageParser;
-import android.content.pm.PackageUserState;
-import android.content.pm.SELinuxUtil;
import android.content.pm.Signature;
+import android.content.pm.PackageParser.SigningDetails;
import android.os.Environment;
import android.util.Slog;
import android.util.Xml;
@@ -453,7 +452,8 @@
public String getMatchedSeInfo(PackageParser.Package pkg) {
// Check for exact signature matches across all certs.
Signature[] certs = mCerts.toArray(new Signature[0]);
- if (!Signature.areExactMatch(certs, pkg.mSignatures)) {
+ if (pkg.mSigningDetails != SigningDetails.UNKNOWN
+ && !Signature.areExactMatch(certs, pkg.mSigningDetails.signatures)) {
return null;
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 4cf1814..ecbc452 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -227,6 +227,7 @@
private static final String ATTR_INSTALL_REASON = "install-reason";
private static final String ATTR_INSTANT_APP = "instant-app";
private static final String ATTR_VIRTUAL_PRELOAD = "virtual-preload";
+ private static final String ATTR_HARMFUL_APP_WARNING = "harmful-app-warning";
private static final String ATTR_PACKAGE_NAME = "packageName";
private static final String ATTR_FINGERPRINT = "fingerprint";
@@ -742,7 +743,8 @@
null /*enabledComponents*/,
null /*disabledComponents*/,
INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED,
- 0, PackageManager.INSTALL_REASON_UNKNOWN);
+ 0, PackageManager.INSTALL_REASON_UNKNOWN,
+ null /*harmfulAppWarning*/);
}
}
}
@@ -783,11 +785,12 @@
*/
static void updatePackageSetting(@NonNull PackageSetting pkgSetting,
@Nullable PackageSetting disabledPkg, @Nullable SharedUserSetting sharedUser,
- @NonNull File codePath, @Nullable String legacyNativeLibraryPath,
- @Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi,
- int pkgFlags, int pkgPrivateFlags, @Nullable List<String> childPkgNames,
- @NonNull UserManagerService userManager, @Nullable String[] usesStaticLibraries,
- @Nullable long[] usesStaticLibrariesVersions) throws PackageManagerException {
+ @NonNull File codePath, File resourcePath,
+ @Nullable String legacyNativeLibraryPath, @Nullable String primaryCpuAbi,
+ @Nullable String secondaryCpuAbi, int pkgFlags, int pkgPrivateFlags,
+ @Nullable List<String> childPkgNames, @NonNull UserManagerService userManager,
+ @Nullable String[] usesStaticLibraries, @Nullable long[] usesStaticLibrariesVersions)
+ throws PackageManagerException {
final String pkgName = pkgSetting.name;
if (pkgSetting.sharedUser != sharedUser) {
PackageManagerService.reportSettingsProblem(Log.WARN,
@@ -799,29 +802,19 @@
}
if (!pkgSetting.codePath.equals(codePath)) {
- // Check to see if its a disabled system app
- if ((pkgSetting.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- // This is an updated system app with versions in both system
- // and data partition. Just let the most recent version
- // take precedence.
- Slog.w(PackageManagerService.TAG,
- "Trying to update system app code path from "
- + pkgSetting.codePathString + " to " + codePath.toString());
- } else {
- // Just a change in the code path is not an issue, but
- // let's log a message about it.
- Slog.i(PackageManagerService.TAG,
- "Package " + pkgName + " codePath changed from "
- + pkgSetting.codePath + " to " + codePath
- + "; Retaining data and using new");
-
- // The owner user's installed flag is set false
- // when the application was installed by other user
- // and the installed flag is not updated
- // when the application is appended as system app later.
- if ((pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0
- && disabledPkg == null) {
- List<UserInfo> allUserInfos = getAllUsers(userManager);
+ final boolean isSystem = pkgSetting.isSystem();
+ Slog.i(PackageManagerService.TAG,
+ "Update" + (isSystem ? " system" : "")
+ + " package " + pkgName
+ + " code path from " + pkgSetting.codePathString
+ + " to " + codePath.toString()
+ + "; Retain data and using new");
+ if (!isSystem) {
+ // The package isn't considered as installed if the application was
+ // first installed by another user. Update the installed flag when the
+ // application ever becomes part of the system.
+ if ((pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0 && disabledPkg == null) {
+ final List<UserInfo> allUserInfos = getAllUsers(userManager);
if (allUserInfos != null) {
for (UserInfo userInfo : allUserInfos) {
pkgSetting.setInstalled(true, userInfo.id);
@@ -829,14 +822,24 @@
}
}
- /*
- * Since we've changed paths, we need to prefer the new
- * native library path over the one stored in the
- * package settings since we might have moved from
- * internal to external storage or vice versa.
- */
+ // Since we've changed paths, prefer the new native library path over
+ // the one stored in the package settings since we might have moved from
+ // internal to external storage or vice versa.
pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath;
}
+ pkgSetting.codePath = codePath;
+ pkgSetting.codePathString = codePath.toString();
+ }
+ if (!pkgSetting.resourcePath.equals(resourcePath)) {
+ final boolean isSystem = pkgSetting.isSystem();
+ Slog.i(PackageManagerService.TAG,
+ "Update" + (isSystem ? " system" : "")
+ + " package " + pkgName
+ + " resource path from " + pkgSetting.resourcePathString
+ + " to " + resourcePath.toString()
+ + "; Retain data and using new");
+ pkgSetting.resourcePath = resourcePath;
+ pkgSetting.resourcePathString = resourcePath.toString();
}
// If what we are scanning is a system (and possibly privileged) package,
// then make it so, regardless of whether it was previously installed only
@@ -853,13 +856,14 @@
if (childPkgNames != null) {
pkgSetting.childPackageNames = new ArrayList<>(childPkgNames);
}
- if (usesStaticLibraries != null) {
- pkgSetting.usesStaticLibraries = Arrays.copyOf(usesStaticLibraries,
- usesStaticLibraries.length);
- }
- if (usesStaticLibrariesVersions != null) {
- pkgSetting.usesStaticLibrariesVersions = Arrays.copyOf(usesStaticLibrariesVersions,
- usesStaticLibrariesVersions.length);
+ // Update static shared library dependencies if needed
+ if (usesStaticLibraries != null && usesStaticLibrariesVersions != null
+ && usesStaticLibraries.length == usesStaticLibrariesVersions.length) {
+ pkgSetting.usesStaticLibraries = usesStaticLibraries;
+ pkgSetting.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
+ } else {
+ pkgSetting.usesStaticLibraries = null;
+ pkgSetting.usesStaticLibrariesVersions = null;
}
}
@@ -912,69 +916,17 @@
userId);
}
+ // TODO: Move to scanPackageOnlyLI() after verifying signatures are setup correctly
+ // by that time.
void insertPackageSettingLPw(PackageSetting p, PackageParser.Package pkg) {
- p.pkg = pkg;
- // pkg.mSetEnabled = p.getEnabled(userId);
- // pkg.mSetStopped = p.getStopped(userId);
- final String volumeUuid = pkg.applicationInfo.volumeUuid;
- final String codePath = pkg.applicationInfo.getCodePath();
- final String resourcePath = pkg.applicationInfo.getResourcePath();
- final String legacyNativeLibraryPath = pkg.applicationInfo.nativeLibraryRootDir;
- // Update volume if needed
- if (!Objects.equals(volumeUuid, p.volumeUuid)) {
- Slog.w(PackageManagerService.TAG, "Volume for " + p.pkg.packageName +
- " changing from " + p.volumeUuid + " to " + volumeUuid);
- p.volumeUuid = volumeUuid;
- }
- // Update code path if needed
- if (!Objects.equals(codePath, p.codePathString)) {
- Slog.w(PackageManagerService.TAG, "Code path for " + p.pkg.packageName +
- " changing from " + p.codePathString + " to " + codePath);
- p.codePath = new File(codePath);
- p.codePathString = codePath;
- }
- //Update resource path if needed
- if (!Objects.equals(resourcePath, p.resourcePathString)) {
- Slog.w(PackageManagerService.TAG, "Resource path for " + p.pkg.packageName +
- " changing from " + p.resourcePathString + " to " + resourcePath);
- p.resourcePath = new File(resourcePath);
- p.resourcePathString = resourcePath;
- }
- // Update the native library paths if needed
- if (!Objects.equals(legacyNativeLibraryPath, p.legacyNativeLibraryPathString)) {
- p.legacyNativeLibraryPathString = legacyNativeLibraryPath;
- }
-
- // Update the required Cpu Abi
- p.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
- p.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
- p.cpuAbiOverrideString = pkg.cpuAbiOverride;
- // Update version code if needed
- if (pkg.getLongVersionCode() != p.versionCode) {
- p.versionCode = pkg.getLongVersionCode();
- }
// Update signatures if needed.
if (p.signatures.mSignatures == null) {
- p.signatures.assignSignatures(pkg.mSignatures);
- }
- // Update flags if needed.
- if (pkg.applicationInfo.flags != p.pkgFlags) {
- p.pkgFlags = pkg.applicationInfo.flags;
+ p.signatures.assignSignatures(pkg.mSigningDetails);
}
// If this app defines a shared user id initialize
// the shared user signatures as well.
if (p.sharedUser != null && p.sharedUser.signatures.mSignatures == null) {
- p.sharedUser.signatures.assignSignatures(pkg.mSignatures);
- }
- // Update static shared library dependencies if needed
- if (pkg.usesStaticLibraries != null && pkg.usesStaticLibrariesVersions != null
- && pkg.usesStaticLibraries.size() == pkg.usesStaticLibrariesVersions.length) {
- p.usesStaticLibraries = new String[pkg.usesStaticLibraries.size()];
- pkg.usesStaticLibraries.toArray(p.usesStaticLibraries);
- p.usesStaticLibrariesVersions = pkg.usesStaticLibrariesVersions;
- } else {
- p.usesStaticLibraries = null;
- p.usesStaticLibrariesVersions = null;
+ p.sharedUser.signatures.assignSignatures(pkg.mSigningDetails);
}
addPackageSettingLPw(p, p.sharedUser);
}
@@ -1680,7 +1632,8 @@
null /*enabledComponents*/,
null /*disabledComponents*/,
INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED,
- 0, PackageManager.INSTALL_REASON_UNKNOWN);
+ 0, PackageManager.INSTALL_REASON_UNKNOWN,
+ null /*harmfulAppWarning*/);
}
return;
}
@@ -1755,7 +1708,8 @@
COMPONENT_ENABLED_STATE_DEFAULT);
final String enabledCaller = parser.getAttributeValue(null,
ATTR_ENABLED_CALLER);
-
+ final String harmfulAppWarning =
+ parser.getAttributeValue(null, ATTR_HARMFUL_APP_WARNING);
final int verifState = XmlUtils.readIntAttribute(parser,
ATTR_DOMAIN_VERIFICATON_STATE,
PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
@@ -1792,7 +1746,7 @@
ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
hidden, suspended, instantApp, virtualPreload, enabledCaller,
enabledComponents, disabledComponents, verifState, linkGeneration,
- installReason);
+ installReason, harmfulAppWarning);
} else if (tagName.equals("preferred-activities")) {
readPreferredActivitiesLPw(parser, userId);
} else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
@@ -2125,6 +2079,10 @@
serializer.attribute(null, ATTR_INSTALL_REASON,
Integer.toString(ustate.installReason));
}
+ if (ustate.harmfulAppWarning != null) {
+ serializer.attribute(null, ATTR_HARMFUL_APP_WARNING,
+ ustate.harmfulAppWarning);
+ }
if (!ArrayUtils.isEmpty(ustate.enabledComponents)) {
serializer.startTag(null, TAG_ENABLED_COMPONENTS);
for (final String name : ustate.enabledComponents) {
@@ -4347,6 +4305,22 @@
return false;
}
+ void setHarmfulAppWarningLPw(String packageName, CharSequence warning, int userId) {
+ final PackageSetting pkgSetting = mPackages.get(packageName);
+ if (pkgSetting == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ pkgSetting.setHarmfulAppWarning(userId, warning == null ? null : warning.toString());
+ }
+
+ String getHarmfulAppWarningLPr(String packageName, int userId) {
+ final PackageSetting pkgSetting = mPackages.get(packageName);
+ if (pkgSetting == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ return pkgSetting.getHarmfulAppWarning(userId);
+ }
+
private static List<UserInfo> getAllUsers(UserManagerService userManager) {
long id = Binder.clearCallingIdentity();
try {
@@ -4493,11 +4467,14 @@
pw.print(ps.getNotLaunched(user.id) ? "l" : "L");
pw.print(ps.getInstantApp(user.id) ? "IA" : "ia");
pw.print(ps.getVirtulalPreload(user.id) ? "VPI" : "vpi");
+ String harmfulAppWarning = ps.getHarmfulAppWarning(user.id);
+ pw.print(harmfulAppWarning != null ? "HA" : "ha");
pw.print(",");
pw.print(ps.getEnabled(user.id));
String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id);
pw.print(",");
pw.print(lastDisabledAppCaller != null ? lastDisabledAppCaller : "?");
+ pw.print(",");
pw.println();
}
return;
@@ -4565,10 +4542,8 @@
}
pw.print(prefix); pw.print(" versionName="); pw.println(ps.pkg.mVersionName);
pw.print(prefix); pw.print(" splits="); dumpSplitNames(pw, ps.pkg); pw.println();
- final int apkSigningVersion = PackageParser.getApkSigningVersion(ps.pkg);
- if (apkSigningVersion != PackageParser.APK_SIGNING_UNKNOWN) {
- pw.print(prefix); pw.print(" apkSigningVersion="); pw.println(apkSigningVersion);
- }
+ final int apkSigningVersion = ps.pkg.mSigningDetails.signatureSchemeVersion;
+ pw.print(prefix); pw.print(" apkSigningVersion="); pw.println(apkSigningVersion);
pw.print(prefix); pw.print(" applicationInfo=");
pw.println(ps.pkg.applicationInfo.toString());
pw.print(prefix); pw.print(" flags="); printFlags(pw, ps.pkg.applicationInfo.flags,
@@ -4772,6 +4747,12 @@
.getRuntimePermissionStates(user.id), dumpAll);
}
+ String harmfulAppWarning = ps.getHarmfulAppWarning(user.id);
+ if (harmfulAppWarning != null) {
+ pw.print(prefix); pw.print(" harmfulAppWarning: ");
+ pw.println(harmfulAppWarning);
+ }
+
if (permissionNames == null) {
ArraySet<String> cmp = ps.getDisabledComponents(user.id);
if (cmp != null && cmp.size() > 0) {
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 877da14..2446131 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
import android.service.pm.PackageServiceDumpProto;
import android.util.ArraySet;
@@ -102,4 +103,8 @@
}
return pkgList;
}
+
+ public boolean isPrivileged() {
+ return (this.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+ }
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 7d57566..92fd904 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -394,7 +394,7 @@
/**
* Start an {@link IntentSender} when user is unlocked after disabling quiet mode.
*
- * @see {@link #trySetQuietModeEnabled(String, boolean, int, IntentSender)}
+ * @see {@link #requestQuietModeEnabled(String, boolean, int, IntentSender)}
*/
private class DisableQuietModeUserUnlockedCallback extends IProgressListener.Stub {
private final IntentSender mTarget;
@@ -823,7 +823,7 @@
}
@Override
- public boolean trySetQuietModeEnabled(@NonNull String callingPackage, boolean enableQuietMode,
+ public boolean requestQuietModeEnabled(@NonNull String callingPackage, boolean enableQuietMode,
int userHandle, @Nullable IntentSender target) {
Preconditions.checkNotNull(callingPackage);
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index bfba700..cc07d82 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -25,12 +25,14 @@
import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
+import android.provider.Settings;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.util.Log;
@@ -116,7 +118,11 @@
UserManager.DISALLOW_USER_SWITCH,
UserManager.DISALLOW_UNIFIED_PASSWORD,
UserManager.DISALLOW_CONFIG_LOCATION_MODE,
- UserManager.DISALLOW_AIRPLANE_MODE
+ UserManager.DISALLOW_AIRPLANE_MODE,
+ UserManager.DISALLOW_CONFIG_BRIGHTNESS,
+ UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE,
+ UserManager.DISALLOW_AMBIENT_DISPLAY,
+ UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT
});
/**
@@ -539,6 +545,22 @@
android.provider.Settings.Global.SAFE_BOOT_DISALLOWED,
newValue ? 1 : 0);
break;
+ case UserManager.DISALLOW_AIRPLANE_MODE:
+ if (newValue) {
+ final boolean airplaneMode = Settings.Global.getInt(
+ context.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
+ if (airplaneMode) {
+ android.provider.Settings.Global.putInt(
+ context.getContentResolver(),
+ android.provider.Settings.Global.AIRPLANE_MODE_ON, 0);
+ // Post the intent.
+ Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ intent.putExtra("state", 0);
+ context.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+ }
+ break;
}
} finally {
Binder.restoreCallingIdentity(id);
diff --git a/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java
index d6281c5..a517d6d 100644
--- a/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java
@@ -111,6 +111,7 @@
final long ident = mInjector.clearCallingIdentity();
try {
+ launchIntent.setPackage(null);
launchIntent.setComponent(component);
mContext.startActivityAsUser(launchIntent,
ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle(), user);
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 34c3ce3..6e07eaa 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -27,7 +27,6 @@
import android.companion.CompanionDeviceManager;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageList;
@@ -62,8 +61,6 @@
import android.util.Xml;
import com.android.internal.util.XmlUtils;
import com.android.server.LocalServices;
-import com.android.server.pm.PackageManagerService;
-import com.android.server.pm.PackageSetting;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -1169,7 +1166,8 @@
final String systemPackageName = mServiceInternal.getKnownPackageName(
PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM);
final PackageParser.Package systemPackage = getPackage(systemPackageName);
- return compareSignatures(systemPackage.mSignatures, pkg.mSignatures)
+ return compareSignatures(systemPackage.mSigningDetails.signatures,
+ pkg.mSigningDetails.signatures)
== PackageManager.SIGNATURE_MATCH;
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 90ac4ab..786b998 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -29,7 +29,6 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
@@ -56,21 +55,17 @@
import android.util.Slog;
import android.util.SparseArray;
-import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.RoSystemProperties;
import com.android.internal.util.ArrayUtils;
-import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemConfig;
import com.android.server.Watchdog;
-import com.android.server.pm.PackageManagerService;
import com.android.server.pm.PackageManagerServiceUtils;
import com.android.server.pm.PackageSetting;
-import com.android.server.pm.ProcessLoggingHandler;
import com.android.server.pm.SharedUserSetting;
import com.android.server.pm.UserManagerService;
import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
@@ -1015,10 +1010,10 @@
final PackageParser.Package systemPackage =
mPackageManagerInt.getPackage(systemPackageName);
boolean allowed = (PackageManagerServiceUtils.compareSignatures(
- bp.getSourceSignatures(), pkg.mSignatures)
+ bp.getSourceSignatures(), pkg.mSigningDetails.signatures)
== PackageManager.SIGNATURE_MATCH)
|| (PackageManagerServiceUtils.compareSignatures(
- systemPackage.mSignatures, pkg.mSignatures)
+ systemPackage.mSigningDetails.signatures, pkg.mSigningDetails.signatures)
== PackageManager.SIGNATURE_MATCH);
if (!allowed && (privilegedPermission || oemPermission)) {
if (pkg.isSystem()) {
diff --git a/services/core/java/com/android/server/policy/BarController.java b/services/core/java/com/android/server/policy/BarController.java
index 10d9565..c906705 100644
--- a/services/core/java/com/android/server/policy/BarController.java
+++ b/services/core/java/com/android/server/policy/BarController.java
@@ -16,13 +16,16 @@
package com.android.server.policy;
+import static com.android.server.wm.proto.BarControllerProto.STATE;
+import static com.android.server.wm.proto.BarControllerProto.TRANSIENT_STATE;
+
import android.app.StatusBarManager;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.View;
-import android.view.ViewGroup;
import android.view.WindowManager;
import com.android.server.LocalServices;
@@ -311,6 +314,13 @@
throw new IllegalArgumentException("Unknown state " + state);
}
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(STATE, mState);
+ proto.write(TRANSIENT_STATE, mTransientBarState);
+ proto.end(token);
+ }
+
public void dump(PrintWriter pw, String prefix) {
if (mWin != null) {
pw.print(prefix); pw.println(mTag);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 076c0e4..88e4270 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -48,7 +48,6 @@
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
-import static android.view.WindowManager.LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA;
import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;
@@ -65,6 +64,9 @@
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
@@ -127,6 +129,26 @@
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
+import static com.android.server.wm.proto.WindowManagerPolicyProto.FOCUSED_APP_TOKEN;
+import static com.android.server.wm.proto.WindowManagerPolicyProto.FOCUSED_WINDOW;
+import static com.android.server.wm.proto.WindowManagerPolicyProto.FORCE_STATUS_BAR;
+import static com.android.server.wm.proto.WindowManagerPolicyProto.FORCE_STATUS_BAR_FROM_KEYGUARD;
+import static com.android.server.wm.proto.WindowManagerPolicyProto.KEYGUARD_DELEGATE;
+import static com.android.server.wm.proto.WindowManagerPolicyProto.KEYGUARD_DRAW_COMPLETE;
+import static com.android.server.wm.proto.WindowManagerPolicyProto.KEYGUARD_OCCLUDED;
+import static com.android.server.wm.proto.WindowManagerPolicyProto.KEYGUARD_OCCLUDED_CHANGED;
+import static com.android.server.wm.proto.WindowManagerPolicyProto.KEYGUARD_OCCLUDED_PENDING;
+import static com.android.server.wm.proto.WindowManagerPolicyProto.LAST_SYSTEM_UI_FLAGS;
+import static com.android.server.wm.proto.WindowManagerPolicyProto.NAVIGATION_BAR;
+import static com.android.server.wm.proto.WindowManagerPolicyProto.ORIENTATION;
+import static com.android.server.wm.proto.WindowManagerPolicyProto.ORIENTATION_LISTENER;
+import static com.android.server.wm.proto.WindowManagerPolicyProto.ROTATION;
+import static com.android.server.wm.proto.WindowManagerPolicyProto.ROTATION_MODE;
+import static com.android.server.wm.proto.WindowManagerPolicyProto.SCREEN_ON_FULLY;
+import static com.android.server.wm.proto.WindowManagerPolicyProto.STATUS_BAR;
+import static com.android.server.wm.proto.WindowManagerPolicyProto.TOP_FULLSCREEN_OPAQUE_OR_DIMMING_WINDOW;
+import static com.android.server.wm.proto.WindowManagerPolicyProto.TOP_FULLSCREEN_OPAQUE_WINDOW;
+import static com.android.server.wm.proto.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW_COMPLETE;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -141,6 +163,7 @@
import android.app.UiModeManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
+import android.content.ComponentCallbacks;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -172,7 +195,6 @@
import android.media.IAudioService;
import android.media.session.MediaSessionLegacyHelper;
import android.os.Binder;
-import android.os.Build;
import android.os.Bundle;
import android.os.FactoryTest;
import android.os.Handler;
@@ -247,9 +269,11 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
+import com.android.internal.policy.KeyguardDismissCallback;
import com.android.internal.policy.PhoneWindow;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.ScreenshotHelper;
import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.widget.PointerLocationView;
import com.android.server.GestureLauncherService;
@@ -441,6 +465,7 @@
AccessibilityManager mAccessibilityManager;
BurnInProtectionHelper mBurnInProtectionHelper;
AppOpsManager mAppOpsManager;
+ private ScreenshotHelper mScreenshotHelper;
private boolean mHasFeatureWatch;
private boolean mHasFeatureLeanback;
@@ -469,6 +494,7 @@
WindowState mNavigationBar = null;
boolean mHasNavigationBar = false;
boolean mNavigationBarCanMove = false; // can the navigation bar ever move to the side?
+ @NavigationBarPosition
int mNavigationBarPosition = NAV_BAR_BOTTOM;
int[] mNavigationBarHeightForRotationDefault = new int[4];
int[] mNavigationBarWidthForRotationDefault = new int[4];
@@ -601,8 +627,6 @@
PointerLocationView mPointerLocationView;
- boolean mEmulateDisplayCutout = false;
-
// During layout, the layer at which the doc window is placed.
int mDockLayer;
// During layout, this is the layer of the status bar.
@@ -698,6 +722,9 @@
// Behavior of Back button while in-call and screen on
int mIncallBackBehavior;
+ // Behavior of rotation suggestions. (See Settings.Secure.SHOW_ROTATION_SUGGESTION)
+ int mShowRotationSuggestions;
+
Display mDisplay;
int mLandscapeRotation = 0; // default landscape rotation
@@ -952,11 +979,11 @@
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS), false, this,
UserHandle.USER_ALL);
- resolver.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.POLICY_CONTROL), false, this,
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.SHOW_ROTATION_SUGGESTIONS), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.EMULATE_DISPLAY_CUTOUT), false, this,
+ Settings.Global.POLICY_CONTROL), false, this,
UserHandle.USER_ALL);
updateSettings();
}
@@ -985,23 +1012,41 @@
}
class MyOrientationListener extends WindowOrientationListener {
- private final Runnable mUpdateRotationRunnable = new Runnable() {
+
+ private SparseArray<Runnable> mRunnableCache;
+
+ MyOrientationListener(Context context, Handler handler) {
+ super(context, handler);
+ mRunnableCache = new SparseArray<>(5);
+ }
+
+ private class UpdateRunnable implements Runnable {
+ private final int mRotation;
+ UpdateRunnable(int rotation) {
+ mRotation = rotation;
+ }
+
@Override
public void run() {
// send interaction hint to improve redraw performance
mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
- updateRotation(false);
+ if (showRotationChoice(mCurrentAppOrientation, mRotation)) {
+ sendProposedRotationChangeToStatusBarInternal(mRotation);
+ } else {
+ updateRotation(false);
+ }
}
- };
-
- MyOrientationListener(Context context, Handler handler) {
- super(context, handler);
}
@Override
public void onProposedRotationChanged(int rotation) {
if (localLOGV) Slog.v(TAG, "onProposedRotationChanged, rotation=" + rotation);
- mHandler.post(mUpdateRotationRunnable);
+ Runnable r = mRunnableCache.get(rotation, null);
+ if (r == null){
+ r = new UpdateRunnable(rotation);
+ mRunnableCache.put(rotation, r);
+ }
+ mHandler.post(r);
}
}
MyOrientationListener mOrientationListener;
@@ -1106,7 +1151,11 @@
// orientation for a little bit, which can cause orientation
// changes to lag, so we'd like to keep it always on. (It will
// still be turned off when the screen is off.)
- return false;
+
+ // When locked we can provide rotation suggestions users can approve to change the
+ // current screen rotation. To do this the sensor needs to be running.
+ return mSupportAutoRotation &&
+ mShowRotationSuggestions == Settings.Secure.SHOW_ROTATION_SUGGESTIONS_ENABLED;
}
return mSupportAutoRotation;
}
@@ -1665,7 +1714,9 @@
@Override
public void run() {
- takeScreenshot(mScreenshotType);
+ mScreenshotHelper.takeScreenshot(mScreenshotType,
+ mStatusBar != null && mStatusBar.isVisibleLw(),
+ mNavigationBar != null && mNavigationBar.isVisibleLw(), mHandler);
}
}
@@ -2143,6 +2194,7 @@
mWindowManagerFuncs.notifyKeyguardTrustedChanged();
}
});
+ mScreenshotHelper = new ScreenshotHelper(mContext);
}
/**
@@ -2255,9 +2307,13 @@
// http://developer.android.com/guide/practices/screens_support.html#range
// For car, ignore the dp limitation. It's physically impossible to rotate the car's screen
// so if the orientation is forced, we need to respect that no matter what.
- boolean isCar = mContext.getPackageManager().hasSystemFeature(
+ final boolean isCar = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_AUTOMOTIVE);
- mForceDefaultOrientation = ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar) &&
+ // For TV, it's usually 960dp x 540dp, ignore the size limitation.
+ // so if the orientation is forced, we need to respect that no matter what.
+ final boolean isTv = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_LEANBACK);
+ mForceDefaultOrientation = ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv) &&
res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation) &&
// For debug purposes the next line turns this feature off with:
// $ adb shell setprop config.override_forced_orient true
@@ -2295,6 +2351,16 @@
Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR_DEFAULT,
UserHandle.USER_CURRENT);
+ // Configure rotation suggestions.
+ int showRotationSuggestions = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
+ Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT,
+ UserHandle.USER_CURRENT);
+ if (mShowRotationSuggestions != showRotationSuggestions) {
+ mShowRotationSuggestions = showRotationSuggestions;
+ updateOrientationListenerLp(); // Enable, disable the orientation listener
+ }
+
// Configure wake gesture.
boolean wakeGestureEnabledSetting = Settings.Secure.getIntForUser(resolver,
Settings.Secure.WAKE_GESTURE_ENABLED, 0,
@@ -2344,10 +2410,6 @@
if (mImmersiveModeConfirmation != null) {
mImmersiveModeConfirmation.loadSetting(mCurrentUserId);
}
- mEmulateDisplayCutout = Settings.Global.getInt(resolver,
- Settings.Global.EMULATE_DISPLAY_CUTOUT,
- Settings.Global.EMULATE_DISPLAY_CUTOUT_OFF)
- != Settings.Global.EMULATE_DISPLAY_CUTOUT_OFF;
}
synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
PolicyControl.reloadFromSetting(mContext);
@@ -2683,6 +2745,11 @@
}
@Override
+ public void onOverlayChangedLw() {
+ onConfigurationChanged();
+ }
+
+ @Override
public void onConfigurationChanged() {
// TODO(multi-display): Define policy for secondary displays.
Context uiContext = getSystemUiContext();
@@ -4145,20 +4212,23 @@
if (isKeyguardShowingAndNotOccluded()) {
// don't launch home if keyguard showing
return;
- }
-
- if (!mKeyguardOccluded && mKeyguardDelegate.isInputRestricted()) {
+ } else if (mKeyguardOccluded && mKeyguardDelegate.isShowing()) {
+ mKeyguardDelegate.dismiss(new KeyguardDismissCallback() {
+ @Override
+ public void onDismissSucceeded() throws RemoteException {
+ mHandler.post(() -> {
+ startDockOrHome(true /*fromHomeKey*/, awakenFromDreams);
+ });
+ }
+ }, null /* message */);
+ return;
+ } else if (!mKeyguardOccluded && mKeyguardDelegate.isInputRestricted()) {
// when in keyguard restricted mode, must first verify unlock
// before launching home
mKeyguardDelegate.verifyUnlock(new OnKeyguardExitResult() {
@Override
public void onKeyguardExitResult(boolean success) {
if (success) {
- try {
- ActivityManager.getService().stopAppSwitches();
- } catch (RemoteException e) {
- }
- sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
startDockOrHome(true /*fromHomeKey*/, awakenFromDreams);
}
}
@@ -4168,11 +4238,11 @@
}
// no keyguard stuff to worry about, just launch home!
- try {
- ActivityManager.getService().stopAppSwitches();
- } catch (RemoteException e) {
- }
if (mRecentsVisible) {
+ try {
+ ActivityManager.getService().stopAppSwitches();
+ } catch (RemoteException e) {}
+
// Hide Recents and notify it to launch Home
if (awakenFromDreams) {
awakenDreams();
@@ -4180,7 +4250,6 @@
hideRecentApps(false, true);
} else {
// Otherwise, just launch Home
- sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
startDockOrHome(true /*fromHomeKey*/, awakenFromDreams);
}
}
@@ -4382,7 +4451,7 @@
/** {@inheritDoc} */
@Override
public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {
- displayFrames.onBeginLayout(mEmulateDisplayCutout, mStatusBarHeight);
+ displayFrames.onBeginLayout();
// TODO(multi-display): This doesn't seem right...Maybe only apply to default display?
mSystemGestures.screenWidth = displayFrames.mUnrestricted.width();
mSystemGestures.screenHeight = displayFrames.mUnrestricted.height();
@@ -4564,6 +4633,7 @@
dockFrame.top = displayFrames.mStable.top;
displayFrames.mContent.set(dockFrame);
displayFrames.mVoiceContent.set(dockFrame);
+ displayFrames.mCurrent.set(dockFrame);
if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " + String.format(
"dock=%s content=%s cur=%s", dockFrame.toString(),
@@ -4684,6 +4754,7 @@
return mNavigationBarController.checkHiddenLw();
}
+ @NavigationBarPosition
private int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) {
if (mNavigationBarCanMove && displayWidth > displayHeight) {
if (displayRotation == Surface.ROTATION_270) {
@@ -4801,7 +4872,6 @@
final int type = attrs.type;
final int fl = PolicyControl.getWindowFlags(win, attrs);
- final long fl2 = attrs.flags2;
final int pfl = attrs.privateFlags;
final int sim = attrs.softInputMode;
final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(win, null);
@@ -4823,12 +4893,10 @@
final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0
- || (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0
- || (requestedSysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0;
+ || (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR;
- final boolean layoutInCutout = (fl2 & FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA) != 0;
sf.set(displayFrames.mStable);
@@ -4988,9 +5056,6 @@
// moving from a window that is not hiding the status bar to one that is.
cf.set(displayFrames.mRestricted);
}
- if (requestedFullscreen && !layoutInCutout) {
- pf.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
- }
applyStableConstraints(sysUiFl, fl, cf, displayFrames);
if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
vf.set(displayFrames.mCurrent);
@@ -5076,9 +5141,6 @@
of.set(displayFrames.mUnrestricted);
df.set(displayFrames.mUnrestricted);
pf.set(displayFrames.mUnrestricted);
- if (requestedFullscreen && !layoutInCutout) {
- pf.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
- }
} else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) {
of.set(displayFrames.mRestricted);
df.set(displayFrames.mRestricted);
@@ -5153,15 +5215,18 @@
}
}
- // Ensure that windows that did not request to be laid out in the cutout don't get laid
- // out there.
- if (!layoutInCutout) {
+ final int cutoutMode = attrs.layoutInDisplayCutoutMode;
+ // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in
+ // the cutout safe zone.
+ if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
final Rect displayCutoutSafeExceptMaybeTop = mTmpRect;
displayCutoutSafeExceptMaybeTop.set(displayFrames.mDisplayCutoutSafe);
- if (layoutInScreen && layoutInsetDecor) {
+ if (layoutInScreen && layoutInsetDecor && !requestedFullscreen
+ && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
// At the top we have the status bar, so apps that are
- // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR already expect that there's an inset
- // there and we don't need to exclude the window from that area.
+ // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR but not FULLSCREEN
+ // already expect that there's an inset there and we don't need to exclude
+ // the window from that area.
displayCutoutSafeExceptMaybeTop.top = Integer.MIN_VALUE;
}
pf.intersectUnchecked(displayCutoutSafeExceptMaybeTop);
@@ -5590,11 +5655,7 @@
@Override
public boolean allowAppAnimationsLw() {
- if (mShowingDream) {
- // If keyguard or dreams is currently visible, no reason to animate behind it.
- return false;
- }
- return true;
+ return !mShowingDream;
}
@Override
@@ -5706,100 +5767,6 @@
setHdmiPlugged(!mHdmiPlugged);
}
- final Object mScreenshotLock = new Object();
- ServiceConnection mScreenshotConnection = null;
-
- final Runnable mScreenshotTimeout = new Runnable() {
- @Override public void run() {
- synchronized (mScreenshotLock) {
- if (mScreenshotConnection != null) {
- mContext.unbindService(mScreenshotConnection);
- mScreenshotConnection = null;
- notifyScreenshotError();
- }
- }
- }
- };
-
- // Assume this is called from the Handler thread.
- private void takeScreenshot(final int screenshotType) {
- synchronized (mScreenshotLock) {
- if (mScreenshotConnection != null) {
- return;
- }
- final ComponentName serviceComponent = new ComponentName(SYSUI_PACKAGE,
- SYSUI_SCREENSHOT_SERVICE);
- final Intent serviceIntent = new Intent();
- serviceIntent.setComponent(serviceComponent);
- ServiceConnection conn = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- synchronized (mScreenshotLock) {
- if (mScreenshotConnection != this) {
- return;
- }
- Messenger messenger = new Messenger(service);
- Message msg = Message.obtain(null, screenshotType);
- final ServiceConnection myConn = this;
- Handler h = new Handler(mHandler.getLooper()) {
- @Override
- public void handleMessage(Message msg) {
- synchronized (mScreenshotLock) {
- if (mScreenshotConnection == myConn) {
- mContext.unbindService(mScreenshotConnection);
- mScreenshotConnection = null;
- mHandler.removeCallbacks(mScreenshotTimeout);
- }
- }
- }
- };
- msg.replyTo = new Messenger(h);
- msg.arg1 = msg.arg2 = 0;
- if (mStatusBar != null && mStatusBar.isVisibleLw())
- msg.arg1 = 1;
- if (mNavigationBar != null && mNavigationBar.isVisibleLw())
- msg.arg2 = 1;
- try {
- messenger.send(msg);
- } catch (RemoteException e) {
- }
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- synchronized (mScreenshotLock) {
- if (mScreenshotConnection != null) {
- mContext.unbindService(mScreenshotConnection);
- mScreenshotConnection = null;
- mHandler.removeCallbacks(mScreenshotTimeout);
- notifyScreenshotError();
- }
- }
- }
- };
- if (mContext.bindServiceAsUser(serviceIntent, conn,
- Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
- UserHandle.CURRENT)) {
- mScreenshotConnection = conn;
- mHandler.postDelayed(mScreenshotTimeout, 10000);
- }
- }
- }
-
- /**
- * Notifies the screenshot service to show an error.
- */
- private void notifyScreenshotError() {
- // If the service process is killed, then ask it to clean up after itself
- final ComponentName errorComponent = new ComponentName(SYSUI_PACKAGE,
- SYSUI_SCREENSHOT_ERROR_RECEIVER);
- Intent errorIntent = new Intent(Intent.ACTION_USER_PRESENT);
- errorIntent.setComponent(errorComponent);
- errorIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
- Intent.FLAG_RECEIVER_FOREGROUND);
- mContext.sendBroadcastAsUser(errorIntent, UserHandle.CURRENT);
- }
/** {@inheritDoc} */
@Override
@@ -6224,6 +6191,16 @@
}
/**
+ * Notify the StatusBar that system rotation suggestion has changed.
+ */
+ private void sendProposedRotationChangeToStatusBarInternal(int rotation) {
+ StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+ if (statusBar != null) {
+ statusBar.onProposedRotationChanged(rotation);
+ }
+ }
+
+ /**
* Returns true if the key can have global actions attached to it.
* We reserve all power management keys for the system since they require
* very careful handling.
@@ -6878,12 +6855,12 @@
}
@Override
- public void dismissKeyguardLw(IKeyguardDismissCallback callback) {
+ public void dismissKeyguardLw(IKeyguardDismissCallback callback, CharSequence message) {
if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "PWM.dismissKeyguardLw");
// ask the keyguard to prompt the user to authenticate if necessary
- mKeyguardDelegate.dismiss(callback);
+ mKeyguardDelegate.dismiss(callback, message);
} else if (callback != null) {
try {
callback.onDismissError();
@@ -7165,6 +7142,86 @@
mOrientationListener.setCurrentRotation(rotation);
}
+ public boolean showRotationChoice(int orientation, final int preferredRotation) {
+ // Rotation choice is only shown when the user is in locked mode.
+ if (mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
+
+ // We should only show a rotation choice if:
+ // 1. The rotation isn't forced by the lid, dock, demo, hdmi, vr, etc mode
+ // 2. The user choice won't be ignored due to screen orientation settings
+
+ // Determine if the rotation currently forced
+ if (mForceDefaultOrientation) {
+ return false; // Rotation is forced to default orientation
+
+ } else if (mLidState == LID_OPEN && mLidOpenRotation >= 0) {
+ return false; // Rotation is forced mLidOpenRotation
+
+ } else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR && !mCarDockEnablesAccelerometer) {
+ return false; // Rotation forced to mCarDockRotation
+
+ } else if ((mDockMode == Intent.EXTRA_DOCK_STATE_DESK
+ || mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
+ || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
+ && !mDeskDockEnablesAccelerometer) {
+ return false; // Rotation forced to mDeskDockRotation
+
+ } else if (mHdmiPlugged && mDemoHdmiRotationLock) {
+ return false; // Rotation forced to mDemoHdmiRotation
+
+ } else if (mHdmiPlugged && mDockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
+ && mUndockedHdmiRotation >= 0) {
+ return false; // Rotation forced to mUndockedHdmiRotation
+
+ } else if (mDemoRotationLock) {
+ return false; // Rotation forced to mDemoRotation
+
+ } else if (mPersistentVrModeEnabled) {
+ return false; // Rotation forced to mPortraitRotation
+
+ } else if (!mSupportAutoRotation) {
+ return false;
+ }
+
+ // Determine if the orientation will ignore user choice
+ switch (orientation) {
+ case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
+ case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
+ case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
+ case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
+ case ActivityInfo.SCREEN_ORIENTATION_LOCKED:
+ return false; // Forced into a particular rotation, no user choice
+
+ case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
+ case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
+ case ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR:
+ case ActivityInfo.SCREEN_ORIENTATION_SENSOR:
+ return false; // Sensor overrides user choice
+
+ case ActivityInfo.SCREEN_ORIENTATION_NOSENSOR:
+ // TODO Can sensor be used to indirectly determine the orientation?
+ return false;
+
+ case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
+ // If the user has locked sensor-based rotation, this behaves the same as landscape
+ return false; // User has locked the rotation, will behave as LANDSCAPE
+ case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
+ // If the user has locked sensor-based rotation, this behaves the same as portrait
+ return false; // User has locked the rotation, will behave as PORTRAIT
+ case ActivityInfo.SCREEN_ORIENTATION_USER:
+ // Works with any rotation except upside down
+ return (preferredRotation >= 0) && (preferredRotation != mUpsideDownRotation);
+ case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
+ // Works with any of the 4 rotations
+ return preferredRotation >= 0;
+
+ default:
+ // TODO: how to handle SCREEN_ORIENTATION_BEHIND, UNSET?
+ // For UNSPECIFIED use preferred orientation matching SCREEN_ORIENTATION_USER
+ return (preferredRotation >= 0) && (preferredRotation != mUpsideDownRotation);
+ }
+ }
+
private boolean isLandscapeOrSeascape(int rotation) {
return rotation == mLandscapeRotation || rotation == mSeascapeRotation;
}
@@ -7575,6 +7632,11 @@
}
void startDockOrHome(boolean fromHomeKey, boolean awakenFromDreams) {
+ try {
+ ActivityManager.getService().stopAppSwitches();
+ } catch (RemoteException e) {}
+ sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
+
if (awakenFromDreams) {
awakenDreams();
}
@@ -7614,11 +7676,6 @@
}
if (false) {
// This code always brings home to the front.
- try {
- ActivityManager.getService().stopAppSwitches();
- } catch (RemoteException e) {
- }
- sendCloseSystemWindows();
startDockOrHome(false /*fromHomeKey*/, true /* awakenFromDreams */);
} else {
// This code brings home to the front or, if it is already
@@ -7842,8 +7899,11 @@
// If the top fullscreen-or-dimming window is also the top fullscreen, respect
// its light flag.
vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
- vis |= PolicyControl.getSystemUiVisibility(statusColorWin, null)
- & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ if (!statusColorWin.isLetterboxedForDisplayCutoutLw()) {
+ // Only allow white status bar if the window was not letterboxed.
+ vis |= PolicyControl.getSystemUiVisibility(statusColorWin, null)
+ & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ }
} else if (statusColorWin != null && statusColorWin.isDimming()) {
// Otherwise if it's dimming, clear the light flag.
vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
@@ -7851,26 +7911,60 @@
return vis;
}
- private int updateLightNavigationBarLw(int vis, WindowState opaque,
- WindowState opaqueOrDimming) {
- final WindowState imeWin = mWindowManagerFuncs.getInputMethodWindowLw();
+ @VisibleForTesting
+ @Nullable
+ static WindowState chooseNavigationColorWindowLw(WindowState opaque,
+ WindowState opaqueOrDimming, WindowState imeWindow,
+ @NavigationBarPosition int navBarPosition) {
+ // If the IME window is visible and FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS is set, then IME
+ // window can be navigation color window.
+ final boolean imeWindowCanNavColorWindow = imeWindow != null
+ && imeWindow.isVisibleLw()
+ && navBarPosition == NAV_BAR_BOTTOM
+ && (PolicyControl.getWindowFlags(imeWindow, null)
+ & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
- final WindowState navColorWin;
- if (imeWin != null && imeWin.isVisibleLw() && mNavigationBarPosition == NAV_BAR_BOTTOM) {
- navColorWin = imeWin;
- } else {
- navColorWin = opaqueOrDimming;
+ if (opaque != null && opaqueOrDimming == opaque) {
+ // If the top fullscreen-or-dimming window is also the top fullscreen, respect it
+ // unless IME window is also eligible, since currently the IME window is always show
+ // above the opaque fullscreen app window, regardless of the IME target window.
+ // TODO(b/31559891): Maybe we need to revisit this condition once b/31559891 is fixed.
+ return imeWindowCanNavColorWindow ? imeWindow : opaque;
}
+ if (opaqueOrDimming == null || !opaqueOrDimming.isDimming()) {
+ // No dimming window is involved. Determine the result only with the IME window.
+ return imeWindowCanNavColorWindow ? imeWindow : null;
+ }
+
+ if (!imeWindowCanNavColorWindow) {
+ // No IME window is involved. Determine the result only with opaqueOrDimming.
+ return opaqueOrDimming;
+ }
+
+ // The IME window and the dimming window are competing. Check if the dimming window can be
+ // IME target or not.
+ if (LayoutParams.mayUseInputMethod(PolicyControl.getWindowFlags(opaqueOrDimming, null))) {
+ // The IME window is above the dimming window.
+ return imeWindow;
+ } else {
+ // The dimming window is above the IME window.
+ return opaqueOrDimming;
+ }
+ }
+
+ @VisibleForTesting
+ static int updateLightNavigationBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming,
+ WindowState imeWindow, WindowState navColorWin) {
+
if (navColorWin != null) {
- if (navColorWin == opaque) {
- // If the top fullscreen-or-dimming window is also the top fullscreen, respect
- // its light flag.
+ if (navColorWin == imeWindow || navColorWin == opaque) {
+ // Respect the light flag.
vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
vis |= PolicyControl.getSystemUiVisibility(navColorWin, null)
& View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
- } else if (navColorWin.isDimming() || navColorWin == imeWin) {
- // Otherwise if it's dimming or it's the IME window, clear the light flag.
+ } else if (navColorWin == opaqueOrDimming && navColorWin.isDimming()) {
+ // Clear the light flag for dimming window.
vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
}
}
@@ -8008,8 +8102,12 @@
vis = mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis);
+ final WindowState navColorWin = chooseNavigationColorWindowLw(
+ mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState,
+ mWindowManagerFuncs.getInputMethodWindowLw(), mNavigationBarPosition);
vis = updateLightNavigationBarLw(vis, mTopFullscreenOpaqueWindowState,
- mTopFullscreenOpaqueOrDimmingWindowState);
+ mTopFullscreenOpaqueOrDimmingWindowState,
+ mWindowManagerFuncs.getInputMethodWindowLw(), navColorWin);
return vis;
}
@@ -8177,6 +8275,40 @@
@Override
public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
+ proto.write(LAST_SYSTEM_UI_FLAGS, mLastSystemUiFlags);
+ proto.write(ROTATION_MODE, mUserRotationMode);
+ proto.write(ROTATION, mUserRotation);
+ proto.write(ORIENTATION, mCurrentAppOrientation);
+ proto.write(SCREEN_ON_FULLY, mScreenOnFully);
+ proto.write(KEYGUARD_DRAW_COMPLETE, mKeyguardDrawComplete);
+ proto.write(WINDOW_MANAGER_DRAW_COMPLETE, mWindowManagerDrawComplete);
+ if (mFocusedApp != null) {
+ proto.write(FOCUSED_APP_TOKEN, mFocusedApp.toString());
+ }
+ if (mFocusedWindow != null) {
+ mFocusedWindow.writeIdentifierToProto(proto, FOCUSED_WINDOW);
+ }
+ if (mTopFullscreenOpaqueWindowState != null) {
+ mTopFullscreenOpaqueWindowState.writeIdentifierToProto(
+ proto, TOP_FULLSCREEN_OPAQUE_WINDOW);
+ }
+ if (mTopFullscreenOpaqueOrDimmingWindowState != null) {
+ mTopFullscreenOpaqueOrDimmingWindowState.writeIdentifierToProto(
+ proto, TOP_FULLSCREEN_OPAQUE_OR_DIMMING_WINDOW);
+ }
+ proto.write(KEYGUARD_OCCLUDED, mKeyguardOccluded);
+ proto.write(KEYGUARD_OCCLUDED_CHANGED, mKeyguardOccludedChanged);
+ proto.write(KEYGUARD_OCCLUDED_PENDING, mPendingKeyguardOccluded);
+ proto.write(FORCE_STATUS_BAR, mForceStatusBar);
+ proto.write(FORCE_STATUS_BAR_FROM_KEYGUARD, mForceStatusBarFromKeyguard);
+ mStatusBarController.writeToProto(proto, STATUS_BAR);
+ mNavigationBarController.writeToProto(proto, NAVIGATION_BAR);
+ if (mOrientationListener != null) {
+ mOrientationListener.writeToProto(proto, ORIENTATION_LISTENER);
+ }
+ if (mKeyguardDelegate != null) {
+ mKeyguardDelegate.writeToProto(proto, KEYGUARD_DELEGATE);
+ }
proto.end(token);
}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index cfe4088..c05dd2a 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -61,6 +61,8 @@
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
import android.Manifest;
import android.annotation.IntDef;
import android.annotation.Nullable;
@@ -136,10 +138,9 @@
* </dl>
*/
public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
- // Navigation bar position values
- int NAV_BAR_LEFT = 1 << 0;
- int NAV_BAR_RIGHT = 1 << 1;
- int NAV_BAR_BOTTOM = 1 << 2;
+ @Retention(SOURCE)
+ @IntDef({NAV_BAR_LEFT, NAV_BAR_RIGHT, NAV_BAR_BOTTOM})
+ @interface NavigationBarPosition {}
/**
* Pass this event to the user / app. To be returned from
@@ -170,6 +171,11 @@
void onKeyguardOccludedChangedLw(boolean occluded);
/**
+ * Called when the resource overlays change.
+ */
+ default void onOverlayChangedLw() {}
+
+ /**
* Interface to the Window Manager state associated with a particular
* window. You can hold on to an instance of this interface from the call
* to prepareAddWindow() until removeWindow().
@@ -440,6 +446,13 @@
*/
public boolean isDimming();
+ /**
+ * Returns true if the window is letterboxed for the display cutout.
+ */
+ default boolean isLetterboxedForDisplayCutoutLw() {
+ return false;
+ }
+
/** @return the current windowing mode of this window. */
int getWindowingMode();
@@ -468,6 +481,11 @@
* visible. That is, they have the permission {@link Manifest.permission#DEVICE_POWER}.
*/
boolean canAcquireSleepToken();
+
+ /**
+ * Writes {@link com.android.server.wm.proto.IdentifierProto} to stream.
+ */
+ void writeIdentifierToProto(ProtoOutputStream proto, long fieldId);
}
/**
@@ -1188,13 +1206,12 @@
/**
* Return true if it is okay to perform animations for an app transition
- * that is about to occur. You may return false for this if, for example,
- * the lock screen is currently displayed so the switch should happen
+ * that is about to occur. You may return false for this if, for example,
+ * the dream window is currently displayed so the switch should happen
* immediately.
*/
public boolean allowAppAnimationsLw();
-
/**
* A new window has been focused.
*/
@@ -1374,8 +1391,10 @@
* Ask the policy to dismiss the keyguard, if it is currently shown.
*
* @param callback Callback to be informed about the result.
+ * @param message A message that should be displayed in the keyguard.
*/
- public void dismissKeyguardLw(@Nullable IKeyguardDismissCallback callback);
+ public void dismissKeyguardLw(@Nullable IKeyguardDismissCallback callback,
+ CharSequence message);
/**
* Ask the policy whether the Keyguard has drawn. If the Keyguard is disabled, this method
@@ -1637,6 +1656,7 @@
* @see #NAV_BAR_RIGHT
* @see #NAV_BAR_BOTTOM
*/
+ @NavigationBarPosition
int getNavBarPosition();
/**
diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/policy/WindowOrientationListener.java
index 169fd27..48a196d 100644
--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/policy/WindowOrientationListener.java
@@ -16,6 +16,9 @@
package com.android.server.policy;
+import static com.android.server.wm.proto.WindowOrientationListenerProto.ENABLED;
+import static com.android.server.wm.proto.WindowOrientationListenerProto.ROTATION;
+
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
@@ -24,12 +27,11 @@
import android.os.Handler;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.text.TextUtils;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.Surface;
import java.io.PrintWriter;
-import java.util.Arrays;
import java.util.List;
/**
@@ -65,7 +67,7 @@
/**
* Creates a new WindowOrientationListener.
- *
+ *
* @param context for the WindowOrientationListener.
* @param handler Provides the Looper for receiving sensor updates.
*/
@@ -75,12 +77,12 @@
/**
* Creates a new WindowOrientationListener.
- *
+ *
* @param context for the WindowOrientationListener.
* @param handler Provides the Looper for receiving sensor updates.
* @param rate at which sensor events are processed (see also
* {@link android.hardware.SensorManager SensorManager}). Use the default
- * value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL
+ * value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL
* SENSOR_DELAY_NORMAL} for simple screen orientation change detection.
*
* This constructor is private since no one uses it.
@@ -89,7 +91,28 @@
mHandler = handler;
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
mRate = rate;
- mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_DEVICE_ORIENTATION);
+ List<Sensor> l = mSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION);
+ Sensor wakeUpDeviceOrientationSensor = null;
+ Sensor nonWakeUpDeviceOrientationSensor = null;
+ /**
+ * Prefer the wakeup form of the sensor if implemented.
+ * It's OK to look for just two types of this sensor and use
+ * the last found. Typical devices will only have one sensor of
+ * this type.
+ */
+ for (Sensor s : l) {
+ if (s.isWakeUpSensor()) {
+ wakeUpDeviceOrientationSensor = s;
+ } else {
+ nonWakeUpDeviceOrientationSensor = s;
+ }
+ }
+
+ if (wakeUpDeviceOrientationSensor != null) {
+ mSensor = wakeUpDeviceOrientationSensor;
+ } else {
+ mSensor = nonWakeUpDeviceOrientationSensor;
+ }
if (mSensor != null) {
mOrientationJudge = new OrientationSensorJudge();
@@ -232,6 +255,15 @@
*/
public abstract void onProposedRotationChanged(int rotation);
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ synchronized (mLock) {
+ proto.write(ENABLED, mEnabled);
+ proto.write(ROTATION, mCurrentRotation);
+ }
+ proto.end(token);
+ }
+
public void dump(PrintWriter pw, String prefix) {
synchronized (mLock) {
pw.println(prefix + TAG);
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 58002bc..18f4a3c 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -1,6 +1,11 @@
package com.android.server.policy.keyguard;
import static android.view.Display.INVALID_DISPLAY;
+import static com.android.server.wm.proto.KeyguardServiceDelegateProto.INTERACTIVE_STATE;
+import static com.android.server.wm.proto.KeyguardServiceDelegateProto.OCCLUDED;
+import static com.android.server.wm.proto.KeyguardServiceDelegateProto.SCREEN_STATE;
+import static com.android.server.wm.proto.KeyguardServiceDelegateProto.SECURE;
+import static com.android.server.wm.proto.KeyguardServiceDelegateProto.SHOWING;
import android.app.ActivityManager;
import android.content.ComponentName;
@@ -15,6 +20,7 @@
import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.WindowManagerPolicyConstants;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -257,9 +263,9 @@
mKeyguardState.occluded = isOccluded;
}
- public void dismiss(IKeyguardDismissCallback callback) {
+ public void dismiss(IKeyguardDismissCallback callback, CharSequence message) {
if (mKeyguardService != null) {
- mKeyguardService.dismiss(callback);
+ mKeyguardService.dismiss(callback, message);
}
}
@@ -406,6 +412,16 @@
}
}
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(SHOWING, mKeyguardState.showing);
+ proto.write(OCCLUDED, mKeyguardState.occluded);
+ proto.write(SECURE, mKeyguardState.secure);
+ proto.write(SCREEN_STATE, mKeyguardState.screenState);
+ proto.write(INTERACTIVE_STATE, mKeyguardState.interactiveState);
+ proto.end(token);
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.println(prefix + TAG);
prefix += " ";
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
index 952e0b0..4e84868 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
@@ -74,9 +74,9 @@
}
@Override // Binder interface
- public void dismiss(IKeyguardDismissCallback callback) {
+ public void dismiss(IKeyguardDismissCallback callback, CharSequence message) {
try {
- mService.dismiss(callback);
+ mService.dismiss(callback, message);
} catch (RemoteException e) {
Slog.w(TAG , "Remote Exception", e);
}
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index 6f005a3..a538967 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -20,6 +20,7 @@
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
+import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerSaveState;
import android.provider.Settings;
@@ -49,21 +50,6 @@
public static final boolean DEBUG = false; // DO NOT SUBMIT WITH TRUE.
- /** Value of batterySaverGpsMode such that GPS isn't affected by battery saver mode. */
- public static final int GPS_MODE_NO_CHANGE = 0;
-
- /**
- * Value of batterySaverGpsMode such that GPS is disabled when battery saver mode
- * is enabled and the screen is off.
- */
- public static final int GPS_MODE_DISABLED_WHEN_SCREEN_OFF = 1;
-
- /**
- * Value of batterySaverGpsMode such that location should be disabled altogether
- * when battery saver mode is enabled and the screen is off.
- */
- public static final int GPS_MODE_ALL_DISABLED_WHEN_SCREEN_OFF = 2;
-
// Secure setting for GPS behavior when battery saver mode is on.
public static final String SECURE_KEY_GPS_MODE = "batterySaverGpsMode";
@@ -354,7 +340,7 @@
// Get default value from Settings.Secure
final int defaultGpsMode = Settings.Secure.getInt(mContentResolver, SECURE_KEY_GPS_MODE,
- GPS_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+ PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
mGpsMode = parser.getInt(KEY_GPS_MODE, defaultGpsMode);
// Non-device-specific parameters.
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java
index 0af19b6..bd8baeb 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java
@@ -16,11 +16,11 @@
package com.android.server.power.batterysaver;
import android.content.Context;
+import android.os.PowerManager;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.util.Slog;
-import com.android.server.power.BatterySaverPolicy;
import com.android.server.power.batterysaver.BatterySaverController.Plugin;
public class BatterySaverLocationPlugin implements Plugin {
@@ -53,7 +53,7 @@
private void updateLocationState(BatterySaverController caller) {
final boolean kill =
(caller.getBatterySaverPolicy().getGpsMode()
- == BatterySaverPolicy.GPS_MODE_ALL_DISABLED_WHEN_SCREEN_OFF) &&
+ == PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF) &&
caller.isEnabled() && !caller.isInteractive();
if (DEBUG) {
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index b31f4b3..2f5e2f8 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -555,7 +555,7 @@
case StatsLog.CPU_TIME_PER_FREQ: {
List<StatsLogEventWrapper> ret = new ArrayList();
for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
- long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readDelta();
+ long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute();
if (clusterTimeMs != null) {
for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
@@ -743,9 +743,13 @@
filter.addAction(Intent.ACTION_SHUTDOWN);
mContext.registerReceiverAsUser(
mShutdownEventReceiver, UserHandle.ALL, filter, null, null);
-
- // Pull the latest state of UID->app name, version mapping when statsd starts.
- informAllUidsLocked(mContext);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Pull the latest state of UID->app name, version mapping when statsd starts.
+ informAllUidsLocked(mContext);
+ } finally {
+ restoreCallingIdentity(token);
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
forgetEverything();
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index ca0a450..28fee4e 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -42,6 +42,9 @@
import android.service.trust.TrustAgentService;
import android.util.Log;
import android.util.Slog;
+
+import com.android.internal.policy.IKeyguardDismissCallback;
+
import java.util.Collections;
import java.util.List;
@@ -67,6 +70,7 @@
private static final int MSG_REMOVE_ESCROW_TOKEN = 8;
private static final int MSG_ESCROW_TOKEN_STATE = 9;
private static final int MSG_UNLOCK_USER = 10;
+ private static final int MSG_SHOW_KEYGUARD_ERROR_MESSAGE = 11;
/**
* Time in uptime millis that we wait for the service connection, both when starting
@@ -81,6 +85,7 @@
private static final String DATA_ESCROW_TOKEN = "escrow_token";
private static final String DATA_HANDLE = "handle";
private static final String DATA_USER_ID = "user_id";
+ private static final String DATA_MESSAGE = "message";
private final TrustManagerService mTrustManagerService;
private final int mUserId;
@@ -255,6 +260,11 @@
mTrustManagerService.unlockUserWithToken(handle, eToken, userId);
break;
}
+ case MSG_SHOW_KEYGUARD_ERROR_MESSAGE: {
+ CharSequence message = msg.getData().getCharSequence(DATA_MESSAGE);
+ mTrustManagerService.showKeyguardErrorMessage(message);
+ break;
+ }
}
}
};
@@ -347,6 +357,14 @@
msg.getData().putByteArray(DATA_ESCROW_TOKEN, token);
msg.sendToTarget();
}
+
+ @Override
+ public void showKeyguardErrorMessage(CharSequence message) {
+ if (DEBUG) Slog.d(TAG, "Showing keyguard error message: " + message);
+ Message msg = mHandler.obtainMessage(MSG_SHOW_KEYGUARD_ERROR_MESSAGE);
+ msg.getData().putCharSequence(DATA_MESSAGE, message);
+ msg.sendToTarget();
+ }
};
private final ServiceConnection mConnection = new ServiceConnection() {
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index a7cd962..4413666 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -60,6 +60,7 @@
import android.view.WindowManagerGlobal;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.SystemService;
@@ -249,6 +250,10 @@
mLockPatternUtils.unlockUserWithToken(handle, token, userId);
}
+ void showKeyguardErrorMessage(CharSequence message) {
+ dispatchOnTrustError(message);
+ }
+
void refreshAgentList(int userIdOrAll) {
if (DEBUG) Slog.d(TAG, "refreshAgentList(" + userIdOrAll + ")");
if (!mTrustAgentsCanRun) {
@@ -769,6 +774,23 @@
}
}
+ private void dispatchOnTrustError(CharSequence message) {
+ if (DEBUG) {
+ Log.i(TAG, "onTrustError(" + message + ")");
+ }
+ for (int i = 0; i < mTrustListeners.size(); i++) {
+ try {
+ mTrustListeners.get(i).onTrustError(message);
+ } catch (DeadObjectException e) {
+ Slog.d(TAG, "Removing dead TrustListener.");
+ mTrustListeners.remove(i);
+ i--;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception while notifying TrustListener.", e);
+ }
+ }
+ }
+
// User lifecycle
@Override
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 163b160..659253f 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -346,12 +346,12 @@
final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
if (magnifying) {
switch (transition) {
- case AppTransition.TRANSIT_ACTIVITY_OPEN:
- case AppTransition.TRANSIT_TASK_OPEN:
- case AppTransition.TRANSIT_TASK_TO_FRONT:
- case AppTransition.TRANSIT_WALLPAPER_OPEN:
- case AppTransition.TRANSIT_WALLPAPER_CLOSE:
- case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: {
+ case WindowManager.TRANSIT_ACTIVITY_OPEN:
+ case WindowManager.TRANSIT_TASK_OPEN:
+ case WindowManager.TRANSIT_TASK_TO_FRONT:
+ case WindowManager.TRANSIT_WALLPAPER_OPEN:
+ case WindowManager.TRANSIT_WALLPAPER_CLOSE:
+ case WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN: {
mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index d4b437a..fc7ad09 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -16,6 +16,28 @@
package com.android.server.wm;
+import static android.view.WindowManager.LayoutParams;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_RELAUNCH;
+import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
+import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_UNSET;
+import static android.view.WindowManager.TRANSIT_WALLPAPER_CLOSE;
+import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE;
+import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN;
+import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
import static com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation;
import static com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation;
import static com.android.internal.R.styleable.WindowAnimation_activityOpenEnterAnimation;
@@ -75,9 +97,11 @@
import android.view.AppTransitionAnimationSpec;
import android.view.DisplayListCanvas;
import android.view.IAppTransitionAnimationSpecsFuture;
+import android.view.RemoteAnimationAdapter;
import android.view.RenderNode;
import android.view.ThreadedRenderer;
-import android.view.WindowManager;
+import android.view.WindowManager.TransitionFlags;
+import android.view.WindowManager.TransitionType;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
@@ -109,63 +133,6 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "AppTransition" : TAG_WM;
private static final int CLIP_REVEAL_TRANSLATION_Y_DP = 8;
- /** Not set up for a transition. */
- public static final int TRANSIT_UNSET = -1;
- /** No animation for transition. */
- public static final int TRANSIT_NONE = 0;
- /** A window in a new activity is being opened on top of an existing one in the same task. */
- public static final int TRANSIT_ACTIVITY_OPEN = 6;
- /** The window in the top-most activity is being closed to reveal the
- * previous activity in the same task. */
- public static final int TRANSIT_ACTIVITY_CLOSE = 7;
- /** A window in a new task is being opened on top of an existing one
- * in another activity's task. */
- public static final int TRANSIT_TASK_OPEN = 8;
- /** A window in the top-most activity is being closed to reveal the
- * previous activity in a different task. */
- public static final int TRANSIT_TASK_CLOSE = 9;
- /** A window in an existing task is being displayed on top of an existing one
- * in another activity's task. */
- public static final int TRANSIT_TASK_TO_FRONT = 10;
- /** A window in an existing task is being put below all other tasks. */
- public static final int TRANSIT_TASK_TO_BACK = 11;
- /** A window in a new activity that doesn't have a wallpaper is being opened on top of one that
- * does, effectively closing the wallpaper. */
- public static final int TRANSIT_WALLPAPER_CLOSE = 12;
- /** A window in a new activity that does have a wallpaper is being opened on one that didn't,
- * effectively opening the wallpaper. */
- public static final int TRANSIT_WALLPAPER_OPEN = 13;
- /** A window in a new activity is being opened on top of an existing one, and both are on top
- * of the wallpaper. */
- public static final int TRANSIT_WALLPAPER_INTRA_OPEN = 14;
- /** The window in the top-most activity is being closed to reveal the previous activity, and
- * both are on top of the wallpaper. */
- public static final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15;
- /** A window in a new task is being opened behind an existing one in another activity's task.
- * The new window will show briefly and then be gone. */
- public static final int TRANSIT_TASK_OPEN_BEHIND = 16;
- /** A window in a task is being animated in-place. */
- public static final int TRANSIT_TASK_IN_PLACE = 17;
- /** An activity is being relaunched (e.g. due to configuration change). */
- public static final int TRANSIT_ACTIVITY_RELAUNCH = 18;
- /** A task is being docked from recents. */
- public static final int TRANSIT_DOCK_TASK_FROM_RECENTS = 19;
- /** Keyguard is going away */
- public static final int TRANSIT_KEYGUARD_GOING_AWAY = 20;
- /** Keyguard is going away with showing an activity behind that requests wallpaper */
- public static final int TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER = 21;
- /** Keyguard is being occluded */
- public static final int TRANSIT_KEYGUARD_OCCLUDE = 22;
- /** Keyguard is being unoccluded */
- public static final int TRANSIT_KEYGUARD_UNOCCLUDE = 23;
-
- /** Transition flag: Keyguard is going away, but keeping the notification shade open */
- public static final int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE = 0x1;
- /** Transition flag: Keyguard is going away, but doesn't want an animation for it */
- public static final int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION = 0x2;
- /** Transition flag: Keyguard is going away while it was showing the system wallpaper. */
- public static final int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER = 0x4;
-
/** Fraction of animation at which the recents thumbnail stays completely transparent */
private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.5f;
/** Fraction of animation at which the recents thumbnail becomes completely transparent */
@@ -191,8 +158,8 @@
private final Context mContext;
private final WindowManagerService mService;
- private int mNextAppTransition = TRANSIT_UNSET;
- private int mNextAppTransitionFlags = 0;
+ private @TransitionType int mNextAppTransition = TRANSIT_UNSET;
+ private @TransitionFlags int mNextAppTransitionFlags = 0;
private int mLastUsedAppTransition = TRANSIT_UNSET;
private String mLastOpeningApp;
private String mLastClosingApp;
@@ -213,6 +180,8 @@
* }.
*/
private static final int NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS = 9;
+ private static final int NEXT_TRANSIT_TYPE_REMOTE = 10;
+
private int mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
// These are the possible states for the enter/exit activities during a thumbnail transition
@@ -275,6 +244,8 @@
private final boolean mGridLayoutRecentsEnabled;
private final boolean mLowRamRecentsEnabled;
+ private RemoteAnimationController mRemoteAnimationController;
+
AppTransition(Context context, WindowManagerService service) {
mContext = context;
mService = service;
@@ -321,11 +292,11 @@
return mNextAppTransition != TRANSIT_UNSET;
}
- boolean isTransitionEqual(int transit) {
+ boolean isTransitionEqual(@TransitionType int transit) {
return mNextAppTransition == transit;
}
- int getAppTransition() {
+ @TransitionType int getAppTransition() {
return mNextAppTransition;
}
@@ -454,6 +425,9 @@
app.startDelayingAnimationStart();
}
}
+ if (mRemoteAnimationController != null) {
+ mRemoteAnimationController.goodToGo();
+ }
return redoLayout;
}
@@ -468,6 +442,7 @@
mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
mNextAppTransitionPackage = null;
mNextAppTransitionAnimationsSpecs.clear();
+ mRemoteAnimationController = null;
mNextAppTransitionAnimationsSpecsFuture = null;
mDefaultNextAppTransitionAnimationSpec = null;
mAnimationFinishedCallback = null;
@@ -476,7 +451,7 @@
void freeze() {
final int transit = mNextAppTransition;
- setAppTransition(AppTransition.TRANSIT_UNSET, 0 /* flags */);
+ setAppTransition(TRANSIT_UNSET, 0 /* flags */);
clear();
setReady();
notifyAppTransitionCancelledLocked(transit);
@@ -531,7 +506,7 @@
return redoLayout;
}
- private AttributeCache.Entry getCachedAnimations(WindowManager.LayoutParams lp) {
+ private AttributeCache.Entry getCachedAnimations(LayoutParams lp) {
if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: layout params pkg="
+ (lp != null ? lp.packageName : null)
+ " resId=0x" + (lp != null ? Integer.toHexString(lp.windowAnimations) : null));
@@ -567,7 +542,7 @@
return null;
}
- Animation loadAnimationAttr(WindowManager.LayoutParams lp, int animAttr) {
+ Animation loadAnimationAttr(LayoutParams lp, int animAttr) {
int anim = 0;
Context context = mContext;
if (animAttr >= 0) {
@@ -583,7 +558,7 @@
return null;
}
- Animation loadAnimationRes(WindowManager.LayoutParams lp, int resId) {
+ Animation loadAnimationRes(LayoutParams lp, int resId) {
Context context = mContext;
if (resId >= 0) {
AttributeCache.Entry ent = getCachedAnimations(lp);
@@ -1551,6 +1526,10 @@
&& mNextAppTransition != TRANSIT_KEYGUARD_GOING_AWAY;
}
+ RemoteAnimationController getRemoteAnimationController() {
+ return mRemoteAnimationController;
+ }
+
/**
*
* @param frame These are the bounds of the window when it finishes the animation. This is where
@@ -1572,7 +1551,7 @@
* to the recents thumbnail and hence need to account for the surface being
* bigger.
*/
- Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, int uiMode,
+ Animation loadAnimation(LayoutParams lp, int transit, boolean enter, int uiMode,
int orientation, Rect frame, Rect displayFrame, Rect insets,
@Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean isVoiceInteraction,
boolean freeform, int taskId) {
@@ -1788,7 +1767,7 @@
void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
IRemoteCallback startedCallback) {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
mNextAppTransitionPackage = packageName;
@@ -1796,14 +1775,12 @@
mNextAppTransitionExit = exitAnim;
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
- } else {
- postAnimationCallback();
}
}
void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
int startHeight) {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_SCALE_UP;
putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight, null);
@@ -1813,7 +1790,7 @@
void overridePendingAppTransitionClipReveal(int startX, int startY,
int startWidth, int startHeight) {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CLIP_REVEAL;
putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight, null);
@@ -1823,7 +1800,7 @@
void overridePendingAppTransitionThumb(GraphicBuffer srcThumb, int startX, int startY,
IRemoteCallback startedCallback, boolean scaleUp) {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN;
@@ -1831,14 +1808,12 @@
putDefaultNextAppTransitionCoordinates(startX, startY, 0, 0, srcThumb);
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
- } else {
- postAnimationCallback();
}
}
void overridePendingAppTransitionAspectScaledThumb(GraphicBuffer srcThumb, int startX, int startY,
int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp) {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
@@ -1847,15 +1822,13 @@
srcThumb);
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
- } else {
- postAnimationCallback();
}
}
- public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
+ void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback,
boolean scaleUp) {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
@@ -1878,15 +1851,13 @@
postAnimationCallback();
mNextAppTransitionCallback = onAnimationStartedCallback;
mAnimationFinishedCallback = onAnimationFinishedCallback;
- } else {
- postAnimationCallback();
}
}
void overridePendingAppTransitionMultiThumbFuture(
IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback,
boolean scaleUp) {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
@@ -1896,14 +1867,21 @@
}
}
- void overrideInPlaceAppTransition(String packageName, int anim) {
+ void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) {
if (isTransitionSet()) {
clear();
+ mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE;
+ mRemoteAnimationController = new RemoteAnimationController(mService,
+ remoteAnimationAdapter, mService.mH);
+ }
+ }
+
+ void overrideInPlaceAppTransition(String packageName, int anim) {
+ if (canOverridePendingAppTransition()) {
+ clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE;
mNextAppTransitionPackage = packageName;
mNextAppTransitionInPlace = anim;
- } else {
- postAnimationCallback();
}
}
@@ -1911,13 +1889,18 @@
* @see {@link #NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS}
*/
void overridePendingAppTransitionStartCrossProfileApps() {
- if (isTransitionSet()) {
+ if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS;
postAnimationCallback();
}
}
+ private boolean canOverridePendingAppTransition() {
+ // Remote animations always take precedence
+ return isTransitionSet() && mNextAppTransitionType != NEXT_TRANSIT_TYPE_REMOTE;
+ }
+
/**
* If a future is set for the app transition specs, fetch it in another thread.
*/
@@ -2140,8 +2123,8 @@
* @return true if transition is not running and should not be skipped, false if transition is
* already running
*/
- boolean prepareAppTransitionLocked(int transit, boolean alwaysKeepCurrent, int flags,
- boolean forceOverride) {
+ boolean prepareAppTransitionLocked(@TransitionType int transit, boolean alwaysKeepCurrent,
+ @TransitionFlags int flags, boolean forceOverride) {
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Prepare app transition:"
+ " transit=" + appTransitionToString(transit)
+ " " + this
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index ae9f28b..e9efd4e 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -19,8 +19,8 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS;
-import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
+import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
+import static android.view.WindowManager.TRANSIT_UNSET;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
@@ -32,7 +32,6 @@
import android.app.ActivityManager.TaskSnapshot;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
-import android.graphics.Rect;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
@@ -40,6 +39,8 @@
import android.os.Message;
import android.util.Slog;
import android.view.IApplicationToken;
+import android.view.RemoteAnimationDefinition;
+import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.AttributeCache;
@@ -393,7 +394,7 @@
wtoken.mEnteringAnimation = false;
}
if (mService.mAppTransition.getAppTransition()
- == AppTransition.TRANSIT_TASK_OPEN_BEHIND) {
+ == WindowManager.TRANSIT_TASK_OPEN_BEHIND) {
// We're launchingBehind, add the launching activity to mOpeningApps.
final WindowState win =
mService.getDefaultDisplayContentLocked().findFocusedWindow();
@@ -695,6 +696,17 @@
}
}
+ public void registerRemoteAnimations(RemoteAnimationDefinition definition) {
+ synchronized (mWindowMap) {
+ if (mContainer == null) {
+ Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app"
+ + " token: " + mToken);
+ return;
+ }
+ mContainer.registerRemoteAnimations(definition);
+ }
+ }
+
void reportStartingWindowDrawn() {
mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_STARTING_WINDOW_DRAWN));
}
diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
index 487b52c..db95634 100644
--- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java
+++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
@@ -20,12 +20,16 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
+import static com.android.server.wm.proto.AppWindowThumbnailProto.HEIGHT;
+import static com.android.server.wm.proto.AppWindowThumbnailProto.SURFACE_ANIMATOR;
+import static com.android.server.wm.proto.AppWindowThumbnailProto.WIDTH;
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.os.Binder;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Builder;
@@ -114,6 +118,22 @@
mSurfaceControl.destroy();
}
+ /**
+ * Write to a protocol buffer output stream. Protocol buffer message definition is at {@link
+ * com.android.server.wm.proto.AppWindowThumbnailProto}.
+ *
+ * @param proto Stream to write the AppWindowThumbnail object to.
+ * @param fieldId Field Id of the AppWindowThumbnail as defined in the parent message.
+ * @hide
+ */
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(WIDTH, mWidth);
+ proto.write(HEIGHT, mHeight);
+ mSurfaceAnimator.writeToProto(proto, SURFACE_ANIMATOR);
+ proto.end(token);
+ }
+
@Override
public Transaction getPendingTransaction() {
return mAppToken.getPendingTransaction();
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index fc0564d..261065f 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -24,6 +24,7 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.SurfaceControl.HIDDEN;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
@@ -33,7 +34,7 @@
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
+import static android.view.WindowManager.TRANSIT_UNSET;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
@@ -51,7 +52,28 @@
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.logWithStack;
+import static com.android.server.wm.proto.AppWindowTokenProto.ALL_DRAWN;
+import static com.android.server.wm.proto.AppWindowTokenProto.APP_STOPPED;
+import static com.android.server.wm.proto.AppWindowTokenProto.CLIENT_HIDDEN;
+import static com.android.server.wm.proto.AppWindowTokenProto.DEFER_HIDING_CLIENT;
+import static com.android.server.wm.proto.AppWindowTokenProto.FILLS_PARENT;
+import static com.android.server.wm.proto.AppWindowTokenProto.FROZEN_BOUNDS;
+import static com.android.server.wm.proto.AppWindowTokenProto.HIDDEN_REQUESTED;
+import static com.android.server.wm.proto.AppWindowTokenProto.HIDDEN_SET_FROM_TRANSFERRED_STARTING_WINDOW;
+import static com.android.server.wm.proto.AppWindowTokenProto.IS_REALLY_ANIMATING;
+import static com.android.server.wm.proto.AppWindowTokenProto.IS_WAITING_FOR_TRANSITION_START;
+import static com.android.server.wm.proto.AppWindowTokenProto.LAST_ALL_DRAWN;
+import static com.android.server.wm.proto.AppWindowTokenProto.LAST_SURFACE_SHOWING;
import static com.android.server.wm.proto.AppWindowTokenProto.NAME;
+import static com.android.server.wm.proto.AppWindowTokenProto.NUM_DRAWN_WINDOWS;
+import static com.android.server.wm.proto.AppWindowTokenProto.NUM_INTERESTING_WINDOWS;
+import static com.android.server.wm.proto.AppWindowTokenProto.REMOVED;
+import static com.android.server.wm.proto.AppWindowTokenProto.REPORTED_DRAWN;
+import static com.android.server.wm.proto.AppWindowTokenProto.REPORTED_VISIBLE;
+import static com.android.server.wm.proto.AppWindowTokenProto.STARTING_DISPLAYED;
+import static com.android.server.wm.proto.AppWindowTokenProto.STARTING_MOVED;
+import static com.android.server.wm.proto.AppWindowTokenProto.STARTING_WINDOW;
+import static com.android.server.wm.proto.AppWindowTokenProto.THUMBNAIL;
import static com.android.server.wm.proto.AppWindowTokenProto.WINDOW_TOKEN;
import android.annotation.CallSuper;
@@ -69,6 +91,7 @@
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.IApplicationToken;
+import android.view.RemoteAnimationDefinition;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.WindowManager;
@@ -84,7 +107,6 @@
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.LinkedList;
class AppTokenList extends ArrayList<AppWindowToken> {
}
@@ -224,9 +246,11 @@
/** Whether this token should be boosted at the top of all app window tokens. */
private boolean mNeedsZBoost;
+ private Letterbox mLetterbox;
private final Point mTmpPoint = new Point();
private final Rect mTmpRect = new Rect();
+ private RemoteAnimationDefinition mRemoteAnimationDefinition;
AppWindowToken(WindowManagerService service, IApplicationToken token, boolean voiceInteraction,
DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen,
@@ -355,7 +379,6 @@
// Reset the state of mHiddenSetFromTransferredStartingWindow since visibility is actually
// been set by the app now.
mHiddenSetFromTransferredStartingWindow = false;
- setClientHidden(!visible);
// Allow for state changes and animation to be applied if:
// * token is transitioning visibility state
@@ -371,7 +394,7 @@
boolean runningAppAnimation = false;
- if (transit != AppTransition.TRANSIT_UNSET) {
+ if (transit != WindowManager.TRANSIT_UNSET) {
if (applyAnimationLocked(lp, transit, visible, isVoiceInteraction)) {
delayed = runningAppAnimation = true;
}
@@ -440,6 +463,12 @@
mService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(token);
}
+ // Update the client visibility if we are not running an animation. Otherwise, we'll
+ // update client visibility state in onAnimationFinished.
+ if (!visible && !delayed) {
+ setClientHidden(true);
+ }
+
// If we are hidden but there is no delay needed we immediately
// apply the Surface transaction so that the ActivityManager
// can have some guarantee on the Surface state following
@@ -657,6 +686,7 @@
if (destroyedSomething) {
final DisplayContent dc = getDisplayContent();
dc.assignWindowLayers(true /*setLayoutNeeded*/);
+ updateLetterbox(null);
}
}
@@ -923,6 +953,7 @@
void removeChild(WindowState child) {
super.removeChild(child);
checkKeyguardFlagsChanged();
+ updateLetterbox(child);
}
private boolean waitingForReplacement() {
@@ -1388,6 +1419,33 @@
return isInterestingAndDrawn;
}
+ void updateLetterbox(WindowState winHint) {
+ final WindowState w = findMainWindow();
+ if (w != winHint && winHint != null && w != null) {
+ return;
+ }
+ final boolean needsLetterbox = w != null && w.isLetterboxedAppWindow()
+ && fillsParent() && w.hasDrawnLw();
+ if (needsLetterbox) {
+ if (mLetterbox == null) {
+ mLetterbox = new Letterbox(() -> makeChildSurface(null));
+ }
+ mLetterbox.setDimensions(mPendingTransaction, getParent().getBounds(), w.mFrame);
+ } else if (mLetterbox != null) {
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ // Make sure we have a transaction here, in case we're called outside of a transaction.
+ // This does not use mPendingTransaction, because SurfaceAnimator uses a
+ // global transaction in onAnimationEnd.
+ SurfaceControl.openTransaction();
+ try {
+ mLetterbox.hide(t);
+ } finally {
+ SurfaceControl.mergeToGlobalTransaction(t);
+ SurfaceControl.closeTransaction();
+ }
+ }
+ }
+
@Override
boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
// For legacy reasons we process the TaskStack.mExitingAppTokens first in DisplayContent
@@ -1559,27 +1617,37 @@
// different animation is running.
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AWT#applyAnimationLocked");
if (okToAnimate()) {
- final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
- if (a != null) {
- final TaskStack stack = getStack();
- mTmpPoint.set(0, 0);
- mTmpRect.setEmpty();
- if (stack != null) {
- stack.getRelativePosition(mTmpPoint);
- stack.getBounds(mTmpRect);
- mTmpRect.offsetTo(0, 0);
+ final AnimationAdapter adapter;
+ final TaskStack stack = getStack();
+ mTmpPoint.set(0, 0);
+ mTmpRect.setEmpty();
+ if (stack != null) {
+ stack.getRelativePosition(mTmpPoint);
+ stack.getBounds(mTmpRect);
+ mTmpRect.offsetTo(0, 0);
+ }
+ if (mService.mAppTransition.getRemoteAnimationController() != null) {
+ adapter = mService.mAppTransition.getRemoteAnimationController()
+ .createAnimationAdapter(this, mTmpPoint, mTmpRect);
+ } else {
+ final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
+ if (a != null) {
+ adapter = new LocalAnimationAdapter(
+ new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
+ mService.mAppTransition.canSkipFirstFrame(),
+ mService.mAppTransition.getAppStackClipMode()),
+ mService.mSurfaceAnimationRunner);
+ if (a.getZAdjustment() == Animation.ZORDER_TOP) {
+ mNeedsZBoost = true;
+ }
+ mTransit = transit;
+ mTransitFlags = mService.mAppTransition.getTransitFlags();
+ } else {
+ adapter = null;
}
- final AnimationAdapter adapter = new LocalAnimationAdapter(
- new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
- mService.mAppTransition.canSkipFirstFrame(),
- mService.mAppTransition.getAppStackClipMode()),
- mService.mSurfaceAnimationRunner);
- if (a.getZAdjustment() == Animation.ZORDER_TOP) {
- mNeedsZBoost = true;
- }
+ }
+ if (adapter != null) {
startAnimation(getPendingTransaction(), adapter, !isVisible());
- mTransit = transit;
- mTransitFlags = mService.mAppTransition.getTransitFlags();
}
} else {
cancelAnimation();
@@ -1614,6 +1682,8 @@
// the status bar). In that case we need to use the final frame.
if (freeform) {
frame.set(win.mFrame);
+ } else if (win.isLetterboxedAppWindow()) {
+ frame.set(getTask().getBounds());
} else {
frame.set(win.mContainingFrame);
}
@@ -1700,6 +1770,7 @@
"AppWindowToken");
clearThumbnail();
+ setClientHidden(isHidden());
if (mService.mInputMethodTarget != null && mService.mInputMethodTarget.mAppToken == this) {
getDisplayContent().computeImeTarget(true /* updateImeTarget */);
@@ -1830,6 +1901,14 @@
mThumbnail = null;
}
+ void registerRemoteAnimations(RemoteAnimationDefinition definition) {
+ mRemoteAnimationDefinition = definition;
+ }
+
+ RemoteAnimationDefinition getRemoteAnimationDefinition() {
+ return mRemoteAnimationDefinition;
+ }
+
@Override
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
super.dump(pw, prefix, dumpAll);
@@ -1936,6 +2015,34 @@
final long token = proto.start(fieldId);
writeNameToProto(proto, NAME);
super.writeToProto(proto, WINDOW_TOKEN, trim);
+ proto.write(LAST_SURFACE_SHOWING, mLastSurfaceShowing);
+ proto.write(IS_WAITING_FOR_TRANSITION_START, isWaitingForTransitionStart());
+ proto.write(IS_REALLY_ANIMATING, isReallyAnimating());
+ if (mThumbnail != null){
+ mThumbnail.writeToProto(proto, THUMBNAIL);
+ }
+ proto.write(FILLS_PARENT, mFillsParent);
+ proto.write(APP_STOPPED, mAppStopped);
+ proto.write(HIDDEN_REQUESTED, hiddenRequested);
+ proto.write(CLIENT_HIDDEN, mClientHidden);
+ proto.write(DEFER_HIDING_CLIENT, mDeferHidingClient);
+ proto.write(REPORTED_DRAWN, reportedDrawn);
+ proto.write(REPORTED_VISIBLE, reportedVisible);
+ proto.write(NUM_INTERESTING_WINDOWS, mNumInterestingWindows);
+ proto.write(NUM_DRAWN_WINDOWS, mNumDrawnWindows);
+ proto.write(ALL_DRAWN, allDrawn);
+ proto.write(LAST_ALL_DRAWN, mLastAllDrawn);
+ proto.write(REMOVED, removed);
+ if (startingWindow != null){
+ startingWindow.writeIdentifierToProto(proto, STARTING_WINDOW);
+ }
+ proto.write(STARTING_DISPLAYED, startingDisplayed);
+ proto.write(STARTING_MOVED, startingMoved);
+ proto.write(HIDDEN_SET_FROM_TRANSFERRED_STARTING_WINDOW,
+ mHiddenSetFromTransferredStartingWindow);
+ for (Rect bounds : mFrozenBounds) {
+ bounds.writeToProto(proto, FROZEN_BOUNDS);
+ }
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 252eff5..ef48661 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -62,7 +62,8 @@
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
@@ -121,6 +122,7 @@
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Matrix;
+import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
@@ -137,6 +139,7 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
+import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.InputDevice;
import android.view.MagnificationSpec;
@@ -158,6 +161,7 @@
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -208,6 +212,9 @@
int mInitialDisplayHeight = 0;
int mInitialDisplayDensity = 0;
+ DisplayCutout mInitialDisplayCutout;
+ DisplayCutout mDisplayCutoutOverride;
+
/**
* Overridden display size. Initialized with {@link #mInitialDisplayWidth}
* and {@link #mInitialDisplayHeight}, but can be set via shell command "adb shell wm size".
@@ -335,9 +342,6 @@
new TaskForResizePointSearchResult();
private final ApplySurfaceChangesTransactionState mTmpApplySurfaceChangesTransactionState =
new ApplySurfaceChangesTransactionState();
- private final ScreenshotApplicationState mScreenshotApplicationState =
- new ScreenshotApplicationState();
- private final Transaction mTmpTransaction = new Transaction();
// True if this display is in the process of being removed. Used to determine if the removal of
// the display's direct children should be allowed.
@@ -654,10 +658,7 @@
mWallpaperController.updateWallpaperVisibility();
}
- // Use mTmpTransaction instead of mPendingTransaction because we don't want to commit
- // other changes in mPendingTransaction at this point.
- w.handleWindowMovedIfNeeded(mTmpTransaction);
- SurfaceControl.mergeToGlobalTransaction(mTmpTransaction);
+ w.handleWindowMovedIfNeeded(mPendingTransaction);
final WindowStateAnimator winAnimator = w.mWinAnimator;
@@ -692,37 +693,11 @@
}
}
}
- final TaskStack stack = w.getStack();
- if (!winAnimator.isWaitingForOpening()
- || (stack != null && stack.isAnimatingBounds())) {
- // Updates the shown frame before we set up the surface. This is needed
- // because the resizing could change the top-left position (in addition to
- // size) of the window. setSurfaceBoundariesLocked uses mShownPosition to
- // position the surface.
- //
- // If an animation is being started, we can't call this method because the
- // animation hasn't processed its initial transformation yet, but in general
- // we do want to update the position if the window is animating. We make an exception
- // for the bounds animating state, where an application may have been waiting
- // for an exit animation to start, but instead enters PiP. We need to ensure
- // we always recompute the top-left in this case.
- winAnimator.computeShownFrameLocked();
- }
- winAnimator.setSurfaceBoundariesLocked(mTmpRecoveringMemory /* recoveringMemory */);
-
- // Since setSurfaceBoundariesLocked applies the clipping, we need to apply the position
- // to the surface of the window container and also the position of the stack window
- // container as well. Use mTmpTransaction instead of mPendingTransaction to avoid
- // committing any existing changes in there.
- w.updateSurfacePosition(mTmpTransaction);
- if (stack != null) {
- stack.updateSurfaceBounds(mTmpTransaction);
- }
- SurfaceControl.mergeToGlobalTransaction(mTmpTransaction);
}
final AppWindowToken atoken = w.mAppToken;
if (atoken != null) {
+ atoken.updateLetterbox(w);
final boolean updateAllDrawn = atoken.updateDrawnWindowStates(w);
if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(atoken)) {
mTmpUpdateAllDrawn.add(atoken);
@@ -1193,6 +1168,7 @@
mDisplayInfo.getLogicalMetrics(mRealDisplayMetrics,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
}
+ mDisplayInfo.displayCutout = calculateDisplayCutoutForCurrentRotation();
mDisplayInfo.getAppMetrics(mDisplayMetrics);
if (mDisplayScalingDisabled) {
mDisplayInfo.flags |= Display.FLAG_SCALING_DISABLED;
@@ -1214,6 +1190,18 @@
return mDisplayInfo;
}
+ DisplayCutout calculateDisplayCutoutForCurrentRotation() {
+ final DisplayCutout cutout = mInitialDisplayCutout;
+ if (cutout == null || cutout == DisplayCutout.NO_CUTOUT || mRotation == ROTATION_0) {
+ return cutout;
+ }
+ final Path bounds = cutout.getBounds().getBoundaryPath();
+ transformPhysicalToLogicalCoordinates(mRotation, mInitialDisplayWidth,
+ mInitialDisplayHeight, mTmpMatrix);
+ bounds.transform(mTmpMatrix);
+ return DisplayCutout.fromBounds(bounds);
+ }
+
/**
* Compute display configuration based on display properties and policy settings.
* Do not call if mDisplayReady == false.
@@ -1676,6 +1664,7 @@
mInitialDisplayWidth = mDisplayInfo.logicalWidth;
mInitialDisplayHeight = mDisplayInfo.logicalHeight;
mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi;
+ mInitialDisplayCutout = mDisplayInfo.displayCutout;
}
/**
@@ -1690,10 +1679,12 @@
final int newWidth = rotated ? mDisplayInfo.logicalHeight : mDisplayInfo.logicalWidth;
final int newHeight = rotated ? mDisplayInfo.logicalWidth : mDisplayInfo.logicalHeight;
final int newDensity = mDisplayInfo.logicalDensityDpi;
+ final DisplayCutout newCutout = mDisplayInfo.displayCutout;
final boolean displayMetricsChanged = mInitialDisplayWidth != newWidth
|| mInitialDisplayHeight != newHeight
- || mInitialDisplayDensity != mDisplayInfo.logicalDensityDpi;
+ || mInitialDisplayDensity != mDisplayInfo.logicalDensityDpi
+ || !Objects.equals(mInitialDisplayCutout, newCutout);
if (displayMetricsChanged) {
// Check if display size or density is forced.
@@ -1710,6 +1701,7 @@
mInitialDisplayWidth = newWidth;
mInitialDisplayHeight = newHeight;
mInitialDisplayDensity = newDensity;
+ mInitialDisplayCutout = newCutout;
mService.reconfigureDisplayLocked(this);
}
}
@@ -2821,6 +2813,7 @@
mTmpRecoveringMemory = recoveringMemory;
forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
+ prepareSurfaces();
mService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
mTmpApplySurfaceChangesTransactionState.displayHasContent,
diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java
index 0155712..13d0c86 100644
--- a/services/core/java/com/android/server/wm/DisplayFrames.java
+++ b/services/core/java/com/android/server/wm/DisplayFrames.java
@@ -22,6 +22,7 @@
import static com.android.server.wm.proto.DisplayFramesProto.STABLE_BOUNDS;
import android.annotation.NonNull;
+import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.proto.ProtoOutputStream;
@@ -100,9 +101,12 @@
/** During layout, the current screen borders along which input method windows are placed. */
public final Rect mDock = new Rect();
- /** Definition of the cutout */
+ /** The display cutout used for layout (after rotation) */
@NonNull public DisplayCutout mDisplayCutout = DisplayCutout.NO_CUTOUT;
+ /** The cutout as supplied by display info */
+ @NonNull private DisplayCutout mDisplayInfoCutout = DisplayCutout.NO_CUTOUT;
+
/**
* During layout, the frame that is display-cutout safe, i.e. that does not intersect with it.
*/
@@ -126,9 +130,11 @@
mRotation = info.rotation;
mDisplayInfoOverscan.set(
info.overscanLeft, info.overscanTop, info.overscanRight, info.overscanBottom);
+ mDisplayInfoCutout = info.displayCutout != null
+ ? info.displayCutout : DisplayCutout.NO_CUTOUT;
}
- public void onBeginLayout(boolean emulateDisplayCutout, int statusBarHeight) {
+ public void onBeginLayout() {
switch (mRotation) {
case ROTATION_90:
mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.top;
@@ -166,11 +172,24 @@
mStable.set(mUnrestricted);
mStableFullscreen.set(mUnrestricted);
mCurrent.set(mUnrestricted);
- mDisplayCutout = DisplayCutout.NO_CUTOUT;
+
+ mDisplayCutout = mDisplayInfoCutout.calculateRelativeTo(mOverscan);
mDisplayCutoutSafe.set(Integer.MIN_VALUE, Integer.MIN_VALUE,
Integer.MAX_VALUE, Integer.MAX_VALUE);
- if (emulateDisplayCutout) {
- setEmulatedDisplayCutout((int) (statusBarHeight * 0.8));
+ if (!mDisplayCutout.isEmpty()) {
+ final DisplayCutout c = mDisplayCutout;
+ if (c.getSafeInsetLeft() > 0) {
+ mDisplayCutoutSafe.left = mRestrictedOverscan.left + c.getSafeInsetLeft();
+ }
+ if (c.getSafeInsetTop() > 0) {
+ mDisplayCutoutSafe.top = mRestrictedOverscan.top + c.getSafeInsetTop();
+ }
+ if (c.getSafeInsetRight() > 0) {
+ mDisplayCutoutSafe.right = mRestrictedOverscan.right - c.getSafeInsetRight();
+ }
+ if (c.getSafeInsetBottom() > 0) {
+ mDisplayCutoutSafe.bottom = mRestrictedOverscan.bottom - c.getSafeInsetBottom();
+ }
}
}
@@ -178,55 +197,6 @@
return mDock.bottom - mCurrent.bottom;
}
- private void setEmulatedDisplayCutout(int height) {
- final boolean swappedDimensions = mRotation == ROTATION_90 || mRotation == ROTATION_270;
-
- final int screenWidth = swappedDimensions ? mDisplayHeight : mDisplayWidth;
- final int screenHeight = swappedDimensions ? mDisplayWidth : mDisplayHeight;
-
- final int widthTop = (int) (screenWidth * 0.3);
- final int widthBottom = widthTop - height;
-
- switch (mRotation) {
- case ROTATION_90:
- mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList(
- new Point(0, (screenWidth - widthTop) / 2),
- new Point(height, (screenWidth - widthBottom) / 2),
- new Point(height, (screenWidth + widthBottom) / 2),
- new Point(0, (screenWidth + widthTop) / 2)
- )).calculateRelativeTo(mUnrestricted);
- mDisplayCutoutSafe.left = height;
- break;
- case ROTATION_180:
- mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList(
- new Point((screenWidth - widthTop) / 2, screenHeight),
- new Point((screenWidth - widthBottom) / 2, screenHeight - height),
- new Point((screenWidth + widthBottom) / 2, screenHeight - height),
- new Point((screenWidth + widthTop) / 2, screenHeight)
- )).calculateRelativeTo(mUnrestricted);
- mDisplayCutoutSafe.bottom = screenHeight - height;
- break;
- case ROTATION_270:
- mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList(
- new Point(screenHeight, (screenWidth - widthTop) / 2),
- new Point(screenHeight - height, (screenWidth - widthBottom) / 2),
- new Point(screenHeight - height, (screenWidth + widthBottom) / 2),
- new Point(screenHeight, (screenWidth + widthTop) / 2)
- )).calculateRelativeTo(mUnrestricted);
- mDisplayCutoutSafe.right = screenHeight - height;
- break;
- default:
- mDisplayCutout = DisplayCutout.fromBoundingPolygon(Arrays.asList(
- new Point((screenWidth - widthTop) / 2, 0),
- new Point((screenWidth - widthBottom) / 2, height),
- new Point((screenWidth + widthBottom) / 2, height),
- new Point((screenWidth + widthTop) / 2, 0)
- )).calculateRelativeTo(mUnrestricted);
- mDisplayCutoutSafe.top = height;
- break;
- }
- }
-
public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
mStable.writeToProto(proto, STABLE_BOUNDS);
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 03c0768..0cf8d2f 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -29,7 +29,7 @@
import static android.view.WindowManager.DOCKED_TOP;
import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.server.wm.AppTransition.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_NONE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED;
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 28b4c1d..0171b56 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -136,7 +136,7 @@
outSurface.copyFrom(surface);
final IBinder winBinder = window.asBinder();
IBinder token = new Binder();
- mDragState = new DragState(mService, token, surface, flags, winBinder);
+ mDragState = new DragState(mService, this, token, surface, flags, winBinder);
mDragState.mPid = callerPid;
mDragState.mUid = callerUid;
mDragState.mOriginalAlpha = alpha;
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index b9f437a..1ac9b88 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -42,6 +42,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.IUserManager;
+import android.os.UserManagerInternal;
import android.util.Slog;
import android.view.Display;
import android.view.DragEvent;
@@ -55,6 +56,7 @@
import android.view.animation.Interpolator;
import com.android.internal.view.IDragAndDropPermissions;
+import com.android.server.LocalServices;
import com.android.server.input.InputApplicationHandle;
import com.android.server.input.InputWindowHandle;
@@ -116,10 +118,10 @@
private final Interpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
private Point mDisplaySize = new Point();
- DragState(WindowManagerService service, IBinder token, SurfaceControl surface,
- int flags, IBinder localWin) {
+ DragState(WindowManagerService service, DragDropController controller, IBinder token,
+ SurfaceControl surface, int flags, IBinder localWin) {
mService = service;
- mDragDropController = service.mDragDropController;
+ mDragDropController = controller;
mToken = token;
mSurfaceControl = surface;
mFlags = flags;
@@ -318,15 +320,9 @@
mSourceUserId = UserHandle.getUserId(mUid);
- final IUserManager userManager =
- (IUserManager) ServiceManager.getService(Context.USER_SERVICE);
- try {
- mCrossProfileCopyAllowed = !userManager.getUserRestrictions(mSourceUserId).getBoolean(
- UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Remote Exception calling UserManager: " + e);
- mCrossProfileCopyAllowed = false;
- }
+ final UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
+ mCrossProfileCopyAllowed = !userManager.getUserRestriction(
+ mSourceUserId, UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
@@ -534,7 +530,8 @@
final int targetUserId = UserHandle.getUserId(touchedWin.getOwningUid());
final DragAndDropPermissionsHandler dragAndDropPermissions;
- if ((mFlags & View.DRAG_FLAG_GLOBAL) != 0 && (mFlags & DRAG_FLAGS_URI_ACCESS) != 0) {
+ if ((mFlags & View.DRAG_FLAG_GLOBAL) != 0 && (mFlags & DRAG_FLAGS_URI_ACCESS) != 0
+ && mData != null) {
dragAndDropPermissions = new DragAndDropPermissionsHandler(
mData,
mUid,
@@ -546,7 +543,9 @@
dragAndDropPermissions = null;
}
if (mSourceUserId != targetUserId){
- mData.fixUris(mSourceUserId);
+ if (mData != null) {
+ mData.fixUris(mSourceUserId);
+ }
}
final int myPid = Process.myPid();
final IBinder token = touchedWin.mClient.asBinder();
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
new file mode 100644
index 0000000..8fa79ab
--- /dev/null
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.SurfaceControl.HIDDEN;
+
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+
+import java.util.function.Supplier;
+
+/**
+ * Manages a set of {@link SurfaceControl}s to draw a black letterbox between an
+ * outer rect and an inner rect.
+ */
+public class Letterbox {
+
+ private static final Rect EMPTY_RECT = new Rect();
+
+ private final Supplier<SurfaceControl.Builder> mFactory;
+ private final Rect mOuter = new Rect();
+ private final Rect mInner = new Rect();
+ private final LetterboxSurface mTop = new LetterboxSurface("top");
+ private final LetterboxSurface mLeft = new LetterboxSurface("left");
+ private final LetterboxSurface mBottom = new LetterboxSurface("bottom");
+ private final LetterboxSurface mRight = new LetterboxSurface("right");
+
+ /**
+ * Constructs a Letterbox.
+ *
+ * @param surfaceControlFactory a factory for creating the managed {@link SurfaceControl}s
+ */
+ public Letterbox(Supplier<SurfaceControl.Builder> surfaceControlFactory) {
+ mFactory = surfaceControlFactory;
+ }
+
+ /**
+ * Sets the dimensions of the the letterbox, such that the area between the outer and inner
+ * frames will be covered by black color surfaces.
+ *
+ * @param t a transaction in which to set the dimensions
+ * @param outer the outer frame of the letterbox (this frame will be black, except the area
+ * that intersects with the {code inner} frame).
+ * @param inner the inner frame of the letterbox (this frame will be clear)
+ */
+ public void setDimensions(SurfaceControl.Transaction t, Rect outer, Rect inner) {
+ mOuter.set(outer);
+ mInner.set(inner);
+
+ mTop.setRect(t, outer.left, outer.top, inner.right, inner.top);
+ mLeft.setRect(t, outer.left, inner.top, inner.left, outer.bottom);
+ mBottom.setRect(t, inner.left, inner.bottom, outer.right, outer.bottom);
+ mRight.setRect(t, inner.right, outer.top, outer.right, inner.bottom);
+ }
+
+ /**
+ * Hides the letterbox.
+ *
+ * @param t a transaction in which to hide the letterbox
+ */
+ public void hide(SurfaceControl.Transaction t) {
+ setDimensions(t, EMPTY_RECT, EMPTY_RECT);
+ }
+
+ /**
+ * Destroys the managed {@link SurfaceControl}s.
+ */
+ public void destroy() {
+ mOuter.setEmpty();
+ mInner.setEmpty();
+
+ mTop.destroy();
+ mLeft.destroy();
+ mBottom.destroy();
+ mRight.destroy();
+ }
+
+ private class LetterboxSurface {
+
+ private final String mType;
+ private SurfaceControl mSurface;
+
+ private int mLastLeft = 0;
+ private int mLastTop = 0;
+ private int mLastRight = 0;
+ private int mLastBottom = 0;
+
+ public LetterboxSurface(String type) {
+ mType = type;
+ }
+
+ public void setRect(SurfaceControl.Transaction t,
+ int left, int top, int right, int bottom) {
+ if (mLastLeft == left && mLastTop == top
+ && mLastRight == right && mLastBottom == bottom) {
+ // Nothing changed.
+ return;
+ }
+
+ if (left < right && top < bottom) {
+ if (mSurface == null) {
+ createSurface();
+ }
+ t.setPosition(mSurface, left, top);
+ t.setSize(mSurface, right - left, bottom - top);
+ t.show(mSurface);
+ } else if (mSurface != null) {
+ t.hide(mSurface);
+ }
+
+ mLastLeft = left;
+ mLastTop = top;
+ mLastRight = right;
+ mLastBottom = bottom;
+ }
+
+ private void createSurface() {
+ mSurface = mFactory.get().setName("Letterbox - " + mType)
+ .setFlags(HIDDEN).setColorLayer(true).build();
+ mSurface.setLayer(-1);
+ mSurface.setColor(new float[]{0, 0, 0});
+ }
+
+ public void destroy() {
+ if (mSurface != null) {
+ mSurface.destroy();
+ mSurface = null;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 69cbe46..62519e1 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -360,14 +360,19 @@
* Sets the Ime state and height.
*/
void setAdjustedForIme(boolean adjustedForIme, int imeHeight) {
- // Return early if there is no state change
- if (mIsImeShowing == adjustedForIme && mImeHeight == imeHeight) {
+ // Due to the order of callbacks from the system, we may receive an ime height even when
+ // {@param adjustedForIme} is false, and also a zero height when {@param adjustedForIme}
+ // is true. Instead, ensure that the ime state changes with the height and if the ime is
+ // showing, then the height is non-zero.
+ final boolean imeShowing = adjustedForIme && imeHeight > 0;
+ imeHeight = imeShowing ? imeHeight : 0;
+ if (imeShowing == mIsImeShowing && imeHeight == mImeHeight) {
return;
}
- mIsImeShowing = adjustedForIme;
+ mIsImeShowing = imeShowing;
mImeHeight = imeHeight;
- notifyImeVisibilityChanged(adjustedForIme, imeHeight);
+ notifyImeVisibilityChanged(imeShowing, imeHeight);
notifyMovementBoundsChanged(true /* fromImeAdjustment */);
}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
new file mode 100644
index 0000000..688b4ff
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationFinishedCallback.Stub;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+
+import java.util.ArrayList;
+
+/**
+ * Helper class to run app animations in a remote process.
+ */
+class RemoteAnimationController {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "RemoteAnimationController" : TAG_WM;
+ private static final long TIMEOUT_MS = 2000;
+
+ private final WindowManagerService mService;
+ private final RemoteAnimationAdapter mRemoteAnimationAdapter;
+ private final ArrayList<RemoteAnimationAdapterWrapper> mPendingAnimations = new ArrayList<>();
+ private final Rect mTmpRect = new Rect();
+ private final Handler mHandler;
+
+ private final IRemoteAnimationFinishedCallback mFinishedCallback = new Stub() {
+ @Override
+ public void onAnimationFinished() throws RemoteException {
+ RemoteAnimationController.this.onAnimationFinished();
+ }
+ };
+
+ private final Runnable mTimeoutRunnable = () -> {
+ onAnimationFinished();
+ invokeAnimationCancelled();
+ };
+
+ RemoteAnimationController(WindowManagerService service,
+ RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) {
+ mService = service;
+ mRemoteAnimationAdapter = remoteAnimationAdapter;
+ mHandler = handler;
+ }
+
+ /**
+ * Creates an animation for each individual {@link AppWindowToken}.
+ *
+ * @param appWindowToken The app to animate.
+ * @param position The position app bounds, in screen coordinates.
+ * @param stackBounds The stack bounds of the app.
+ * @return The adapter to be run on the app.
+ */
+ AnimationAdapter createAnimationAdapter(AppWindowToken appWindowToken, Point position,
+ Rect stackBounds) {
+ final RemoteAnimationAdapterWrapper adapter = new RemoteAnimationAdapterWrapper(
+ appWindowToken, position, stackBounds);
+ mPendingAnimations.add(adapter);
+ return adapter;
+ }
+
+ /**
+ * Called when the transition is ready to be started, and all leashes have been set up.
+ */
+ void goodToGo() {
+ mHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MS);
+ try {
+ mRemoteAnimationAdapter.getRunner().onAnimationStart(createAnimations(),
+ mFinishedCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to start remote animation", e);
+ onAnimationFinished();
+ }
+ }
+
+ private RemoteAnimationTarget[] createAnimations() {
+ final RemoteAnimationTarget[] result = new RemoteAnimationTarget[mPendingAnimations.size()];
+ for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+ result[i] = mPendingAnimations.get(i).createRemoteAppAnimation();
+ }
+ return result;
+ }
+
+ private void onAnimationFinished() {
+ mHandler.removeCallbacks(mTimeoutRunnable);
+ synchronized (mService.mWindowMap) {
+ mService.openSurfaceTransaction();
+ try {
+ for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+ final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i);
+ adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
+ }
+ } finally {
+ mService.closeSurfaceTransaction("RemoteAnimationController#finished");
+ }
+ }
+ }
+
+ private void invokeAnimationCancelled() {
+ try {
+ mRemoteAnimationAdapter.getRunner().onAnimationCancelled();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to notify cancel", e);
+ }
+ }
+
+ private class RemoteAnimationAdapterWrapper implements AnimationAdapter {
+
+ private final AppWindowToken mAppWindowToken;
+ private SurfaceControl mCapturedLeash;
+ private OnAnimationFinishedCallback mCapturedFinishCallback;
+ private final Point mPosition = new Point();
+ private final Rect mStackBounds = new Rect();
+
+ RemoteAnimationAdapterWrapper(AppWindowToken appWindowToken, Point position,
+ Rect stackBounds) {
+ mAppWindowToken = appWindowToken;
+ mPosition.set(position.x, position.y);
+ mStackBounds.set(stackBounds);
+ }
+
+ RemoteAnimationTarget createRemoteAppAnimation() {
+ return new RemoteAnimationTarget(mAppWindowToken.getTask().mTaskId, getMode(),
+ mCapturedLeash, !mAppWindowToken.fillsParent(),
+ mAppWindowToken.findMainWindow().mWinAnimator.mLastClipRect,
+ mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds);
+ }
+
+ private int getMode() {
+ if (mService.mOpeningApps.contains(mAppWindowToken)) {
+ return RemoteAnimationTarget.MODE_OPENING;
+ } else {
+ return RemoteAnimationTarget.MODE_CLOSING;
+ }
+ }
+
+ @Override
+ public boolean getDetachWallpaper() {
+ return false;
+ }
+
+ @Override
+ public int getBackgroundColor() {
+ return 0;
+ }
+
+ @Override
+ public void startAnimation(SurfaceControl animationLeash, Transaction t,
+ OnAnimationFinishedCallback finishCallback) {
+
+ // Restore z-layering, position and stack crop until client has a chance to modify it.
+ t.setLayer(animationLeash, mAppWindowToken.getPrefixOrderIndex());
+ t.setPosition(animationLeash, mPosition.x, mPosition.y);
+ mTmpRect.set(mStackBounds);
+ mTmpRect.offsetTo(0, 0);
+ t.setWindowCrop(animationLeash, mTmpRect);
+ mCapturedLeash = animationLeash;
+ mCapturedFinishCallback = finishCallback;
+ }
+
+ @Override
+ public void onAnimationCancelled(SurfaceControl animationLeash) {
+ mPendingAnimations.remove(this);
+ if (mPendingAnimations.isEmpty()) {
+ mHandler.removeCallbacks(mTimeoutRunnable);
+ invokeAnimationCancelled();
+ }
+ }
+
+ @Override
+ public long getDurationHint() {
+ return mRemoteAnimationAdapter.getDuration();
+ }
+
+ @Override
+ public long getStatusBarTransitionsStartTime() {
+ return SystemClock.uptimeMillis()
+ + mRemoteAnimationAdapter.getStatusBarTransitionDelay();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java b/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
index d2cbf88..33e560f 100644
--- a/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
+++ b/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
@@ -33,7 +33,7 @@
// the surface control.
//
// See cts/hostsidetests/../../SurfaceTraceReceiver.java for parsing side.
-class RemoteSurfaceTrace extends SurfaceControlWithBackground {
+class RemoteSurfaceTrace extends SurfaceControl {
static final String TAG = "RemoteSurfaceTrace";
final FileDescriptor mWriteFd;
@@ -42,7 +42,7 @@
final WindowManagerService mService;
final WindowState mWindow;
- RemoteSurfaceTrace(FileDescriptor fd, SurfaceControlWithBackground wrapped,
+ RemoteSurfaceTrace(FileDescriptor fd, SurfaceControl wrapped,
WindowState window) {
super(wrapped);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2a77c92..2cc96c9 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -86,7 +86,6 @@
import static com.android.server.wm.WindowManagerService.logSurface;
import static com.android.server.wm.WindowSurfacePlacer.SET_FORCE_HIDING_CHANGED;
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
-import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN;
import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
@@ -968,9 +967,7 @@
doRequest = true;
}
}
- if ((bulkUpdateParams & SET_TURN_ON_SCREEN) != 0) {
- mService.mTurnOnScreen = true;
- }
+
if ((bulkUpdateParams & SET_WALLPAPER_ACTION_PENDING) != 0) {
mWallpaperActionPending = true;
}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index e67cdba..10f1c3a 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -19,11 +19,14 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.proto.SurfaceAnimatorProto.ANIMATION_ADAPTER;
+import static com.android.server.wm.proto.SurfaceAnimatorProto.ANIMATION_START_DELAYED;
+import static com.android.server.wm.proto.SurfaceAnimatorProto.LEASH;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.util.ArrayMap;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
@@ -309,6 +312,24 @@
return leash;
}
+ /**
+ * Write to a protocol buffer output stream. Protocol buffer message definition is at {@link
+ * com.android.server.wm.proto.SurfaceAnimatorProto}.
+ *
+ * @param proto Stream to write the SurfaceAnimator object to.
+ * @param fieldId Field Id of the SurfaceAnimator as defined in the parent message.
+ * @hide
+ */
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(ANIMATION_ADAPTER, mAnimation != null ? mAnimation.toString() : "null");
+ if (mLeash != null){
+ mLeash.writeToProto(proto, LEASH);
+ }
+ proto.write(ANIMATION_START_DELAYED, mAnimationStartDelayed);
+ proto.end(token);
+ }
+
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mAnimation="); pw.print(mAnimation);
pw.print(" mLeash="); pw.println(mLeash);
diff --git a/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java b/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java
deleted file mode 100644
index 7c5bd43..0000000
--- a/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright (C) 2017 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 static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
-
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.view.Surface;
-import android.view.Surface.OutOfResourcesException;
-import android.view.SurfaceControl;
-
-/**
- * SurfaceControl extension that has black background behind navigation bar area for fullscreen
- * letterboxed apps.
- */
-class SurfaceControlWithBackground extends SurfaceControl {
- // SurfaceControl that holds the background.
- private SurfaceControl mBackgroundControl;
-
- // Flag that defines whether the background should be shown.
- private boolean mVisible;
-
- // Way to communicate with corresponding window.
- private WindowSurfaceController mWindowSurfaceController;
-
- // Rect to hold task bounds when computing metrics for background.
- private Rect mTmpContainerRect = new Rect();
-
- // Last metrics applied to the main SurfaceControl.
- private float mLastWidth, mLastHeight;
- private float mLastDsDx = 1, mLastDsDy = 1;
- private float mLastX, mLastY;
-
- // SurfaceFlinger doesn't support crop rectangles where width or height is non-positive.
- // If we just set an empty crop it will behave as if there is no crop at all.
- // To fix this we explicitly hide the surface and won't let it to be shown.
- private boolean mHiddenForCrop = false;
-
- public SurfaceControlWithBackground(SurfaceControlWithBackground other) {
- super(other);
- mBackgroundControl = other.mBackgroundControl;
- mVisible = other.mVisible;
- mWindowSurfaceController = other.mWindowSurfaceController;
- }
-
- public SurfaceControlWithBackground(String name, SurfaceControl.Builder b,
- int windowType, int w, int h,
- WindowSurfaceController windowSurfaceController) throws OutOfResourcesException {
- super(b.build());
-
- // We should only show background behind app windows that are letterboxed in a task.
- if ((windowType != TYPE_BASE_APPLICATION && windowType != TYPE_APPLICATION_STARTING)
- || !windowSurfaceController.mAnimator.mWin.isLetterboxedAppWindow()) {
- return;
- }
- mWindowSurfaceController = windowSurfaceController;
- mLastWidth = w;
- mLastHeight = h;
- mWindowSurfaceController.getContainerRect(mTmpContainerRect);
- mBackgroundControl = b.setName("Background for - " + name)
- .setSize(mTmpContainerRect.width(), mTmpContainerRect.height())
- .setFormat(OPAQUE)
- .setColorLayer(true)
- .build();
- }
-
- @Override
- public void setAlpha(float alpha) {
- super.setAlpha(alpha);
-
- if (mBackgroundControl == null) {
- return;
- }
- mBackgroundControl.setAlpha(alpha);
- }
-
- @Override
- public void setLayer(int zorder) {
- super.setLayer(zorder);
-
- if (mBackgroundControl == null) {
- return;
- }
- // TODO: Use setRelativeLayer(Integer.MIN_VALUE) when it's fixed.
- mBackgroundControl.setLayer(zorder - 1);
- }
-
- @Override
- public void setPosition(float x, float y) {
- super.setPosition(x, y);
-
- if (mBackgroundControl == null) {
- return;
- }
- mLastX = x;
- mLastY = y;
- updateBgPosition();
- }
-
- private void updateBgPosition() {
- mWindowSurfaceController.getContainerRect(mTmpContainerRect);
- final Rect winFrame = mWindowSurfaceController.mAnimator.mWin.mFrame;
- final float offsetX = (mTmpContainerRect.left - winFrame.left) * mLastDsDx;
- final float offsetY = (mTmpContainerRect.top - winFrame.top) * mLastDsDy;
- mBackgroundControl.setPosition(mLastX + offsetX, mLastY + offsetY);
- }
-
- @Override
- public void setSize(int w, int h) {
- super.setSize(w, h);
-
- if (mBackgroundControl == null) {
- return;
- }
- mLastWidth = w;
- mLastHeight = h;
- mWindowSurfaceController.getContainerRect(mTmpContainerRect);
- mBackgroundControl.setSize(mTmpContainerRect.width(), mTmpContainerRect.height());
- }
-
- @Override
- public void setWindowCrop(Rect crop) {
- super.setWindowCrop(crop);
-
- if (mBackgroundControl == null) {
- return;
- }
- calculateBgCrop(crop);
- mBackgroundControl.setWindowCrop(mTmpContainerRect);
- mHiddenForCrop = mTmpContainerRect.isEmpty();
- updateBackgroundVisibility();
- }
-
- @Override
- public void setFinalCrop(Rect crop) {
- super.setFinalCrop(crop);
-
- if (mBackgroundControl == null) {
- return;
- }
- mWindowSurfaceController.getContainerRect(mTmpContainerRect);
- mBackgroundControl.setFinalCrop(mTmpContainerRect);
- }
-
- /**
- * Compute background crop based on current animation progress for main surface control and
- * update {@link #mTmpContainerRect} with new values.
- */
- private void calculateBgCrop(Rect crop) {
- // Track overall progress of animation by computing cropped portion of status bar.
- final Rect contentInsets = mWindowSurfaceController.mAnimator.mWin.mContentInsets;
- float d = contentInsets.top == 0 ? 0 : (float) crop.top / contentInsets.top;
- if (d > 1.f) {
- // We're running expand animation from launcher, won't compute custom bg crop here.
- mTmpContainerRect.setEmpty();
- return;
- }
-
- // Compute new scaled width and height for background that will depend on current animation
- // progress. Those consist of current crop rect for the main surface + scaled areas outside
- // of letterboxed area.
- // TODO: Because the progress is computed with low precision we're getting smaller values
- // for background width/height then screen size at the end of the animation. Will round when
- // the value is smaller then some empiric epsilon. However, this should be fixed by
- // computing correct frames for letterboxed windows in WindowState.
- d = d < 0.025f ? 0 : d;
- mWindowSurfaceController.getContainerRect(mTmpContainerRect);
- int backgroundWidth = 0, backgroundHeight = 0;
- // Compute additional offset for the background when app window is positioned not at (0,0).
- // E.g. landscape with navigation bar on the left.
- final Rect winFrame = mWindowSurfaceController.mAnimator.mWin.mFrame;
- int offsetX = (int)((winFrame.left - mTmpContainerRect.left) * mLastDsDx),
- offsetY = (int) ((winFrame.top - mTmpContainerRect.top) * mLastDsDy);
-
- // Position and size background.
- final int bgPosition = mWindowSurfaceController.mAnimator.mService.getNavBarPosition();
-
- switch (bgPosition) {
- case NAV_BAR_LEFT:
- backgroundWidth = (int) ((mTmpContainerRect.width() - mLastWidth) * (1 - d) + 0.5);
- backgroundHeight = crop.height();
- offsetX += crop.left - backgroundWidth;
- offsetY += crop.top;
- break;
- case NAV_BAR_RIGHT:
- backgroundWidth = (int) ((mTmpContainerRect.width() - mLastWidth) * (1 - d) + 0.5);
- backgroundHeight = crop.height();
- offsetX += crop.right;
- offsetY += crop.top;
- break;
- case NAV_BAR_BOTTOM:
- backgroundWidth = crop.width();
- backgroundHeight = (int) ((mTmpContainerRect.height() - mLastHeight) * (1 - d)
- + 0.5);
- offsetX += crop.left;
- offsetY += crop.bottom;
- break;
- }
- mTmpContainerRect.set(offsetX, offsetY, offsetX + backgroundWidth,
- offsetY + backgroundHeight);
- }
-
- @Override
- public void setLayerStack(int layerStack) {
- super.setLayerStack(layerStack);
-
- if (mBackgroundControl == null) {
- return;
- }
- mBackgroundControl.setLayerStack(layerStack);
- }
-
- @Override
- public void setOpaque(boolean isOpaque) {
- super.setOpaque(isOpaque);
- updateBackgroundVisibility();
- }
-
- @Override
- public void setSecure(boolean isSecure) {
- super.setSecure(isSecure);
- }
-
- @Override
- public void setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) {
- super.setMatrix(dsdx, dtdx, dtdy, dsdy);
-
- if (mBackgroundControl == null) {
- return;
- }
- mBackgroundControl.setMatrix(dsdx, dtdx, dtdy, dsdy);
- mLastDsDx = dsdx;
- mLastDsDy = dsdy;
- updateBgPosition();
- }
-
- @Override
- public void hide() {
- super.hide();
- mVisible = false;
- updateBackgroundVisibility();
- }
-
- @Override
- public void show() {
- super.show();
- mVisible = true;
- updateBackgroundVisibility();
- }
-
- @Override
- public void destroy() {
- super.destroy();
-
- if (mBackgroundControl == null) {
- return;
- }
- mBackgroundControl.destroy();
- }
-
- @Override
- public void release() {
- super.release();
-
- if (mBackgroundControl == null) {
- return;
- }
- mBackgroundControl.release();
- }
-
- @Override
- public void setTransparentRegionHint(Region region) {
- super.setTransparentRegionHint(region);
-
- if (mBackgroundControl == null) {
- return;
- }
- mBackgroundControl.setTransparentRegionHint(region);
- }
-
- @Override
- public void deferTransactionUntil(IBinder handle, long frame) {
- super.deferTransactionUntil(handle, frame);
-
- if (mBackgroundControl == null) {
- return;
- }
- mBackgroundControl.deferTransactionUntil(handle, frame);
- }
-
- @Override
- public void deferTransactionUntil(Surface barrier, long frame) {
- super.deferTransactionUntil(barrier, frame);
-
- if (mBackgroundControl == null) {
- return;
- }
- mBackgroundControl.deferTransactionUntil(barrier, frame);
- }
-
- private void updateBackgroundVisibility() {
- if (mBackgroundControl == null) {
- return;
- }
- final AppWindowToken appWindowToken = mWindowSurfaceController.mAnimator.mWin.mAppToken;
- if (!mHiddenForCrop && mVisible && appWindowToken != null && appWindowToken.fillsParent()) {
- mBackgroundControl.show();
- } else {
- mBackgroundControl.hide();
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 3c96ca1..0628436 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -22,13 +22,13 @@
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.res.Configuration.EMPTY;
-
import static com.android.server.EventLogTags.WM_TASK_REMOVED;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.proto.TaskProto.APP_WINDOW_TOKENS;
import static com.android.server.wm.proto.TaskProto.BOUNDS;
+import static com.android.server.wm.proto.TaskProto.DEFER_REMOVAL;
import static com.android.server.wm.proto.TaskProto.FILLS_PARENT;
import static com.android.server.wm.proto.TaskProto.ID;
import static com.android.server.wm.proto.TaskProto.TEMP_INSET_BOUNDS;
@@ -670,6 +670,7 @@
proto.write(FILLS_PARENT, matchParentBounds());
getBounds().writeToProto(proto, BOUNDS);
mTempInsetBounds.writeToProto(proto, TEMP_INSET_BOUNDS);
+ proto.write(DEFER_REMOVAL, mDeferRemoval);
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index bdda944..8a36226 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -16,8 +16,8 @@
package com.android.server.wm;
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -31,19 +31,25 @@
import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.DOCKED_TOP;
-
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.proto.StackProto.ADJUSTED_BOUNDS;
+import static com.android.server.wm.proto.StackProto.ADJUSTED_FOR_IME;
+import static com.android.server.wm.proto.StackProto.ADJUST_DIVIDER_AMOUNT;
+import static com.android.server.wm.proto.StackProto.ADJUST_IME_AMOUNT;
import static com.android.server.wm.proto.StackProto.ANIMATION_BACKGROUND_SURFACE_IS_DIMMING;
import static com.android.server.wm.proto.StackProto.BOUNDS;
+import static com.android.server.wm.proto.StackProto.DEFER_REMOVAL;
import static com.android.server.wm.proto.StackProto.FILLS_PARENT;
import static com.android.server.wm.proto.StackProto.ID;
+import static com.android.server.wm.proto.StackProto.MINIMIZE_AMOUNT;
import static com.android.server.wm.proto.StackProto.TASKS;
import static com.android.server.wm.proto.StackProto.WINDOW_CONTAINER;
import android.annotation.CallSuper;
import android.content.res.Configuration;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.RemoteException;
@@ -145,6 +151,7 @@
* For {@link #prepareSurfaces}.
*/
final Rect mTmpDimBoundsRect = new Rect();
+ private final Point mLastSurfaceSize = new Point();
TaskStack(WindowManagerService service, int stackId, StackWindowController controller) {
super(service);
@@ -744,7 +751,13 @@
}
final Rect stackBounds = getBounds();
- transaction.setSize(mSurfaceControl, stackBounds.width(), stackBounds.height());
+ final int width = stackBounds.width();
+ final int height = stackBounds.height();
+ if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) {
+ return;
+ }
+ transaction.setSize(mSurfaceControl, width, height);
+ mLastSurfaceSize.set(width, height);
}
@Override
@@ -1308,6 +1321,12 @@
proto.write(FILLS_PARENT, matchParentBounds());
getRawBounds().writeToProto(proto, BOUNDS);
proto.write(ANIMATION_BACKGROUND_SURFACE_IS_DIMMING, mAnimationBackgroundSurfaceIsShown);
+ proto.write(DEFER_REMOVAL, mDeferRemoval);
+ proto.write(MINIMIZE_AMOUNT, mMinimizeAmount);
+ proto.write(ADJUSTED_FOR_IME, mAdjustedForIme);
+ proto.write(ADJUST_IME_AMOUNT, mAdjustImeAmount);
+ proto.write(ADJUST_DIVIDER_AMOUNT, mAdjustDividerAmount);
+ mAdjustedBounds.writeToProto(proto, ADJUSTED_BOUNDS);
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index ac0919d..1218d3b 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -25,7 +25,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 3efd6ac..cec13ab 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -292,9 +292,6 @@
if ((bulkUpdateParams & WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE) != 0) {
builder.append(" ORIENTATION_CHANGE_COMPLETE");
}
- if ((bulkUpdateParams & WindowSurfacePlacer.SET_TURN_ON_SCREEN) != 0) {
- builder.append(" TURN_ON_SCREEN");
- }
return builder.toString();
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 5548f3d..42c6ec2 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -19,26 +19,26 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.SurfaceControl.Transaction;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.proto.WindowContainerProto.CONFIGURATION_CONTAINER;
import static com.android.server.wm.proto.WindowContainerProto.ORIENTATION;
+import static com.android.server.wm.proto.WindowContainerProto.SURFACE_ANIMATOR;
import static com.android.server.wm.proto.WindowContainerProto.VISIBLE;
-import static android.view.SurfaceControl.Transaction;
import android.annotation.CallSuper;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
+import android.util.Pools;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.MagnificationSpec;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Builder;
import android.view.SurfaceSession;
-import android.util.Pools;
-
-import android.util.proto.ProtoOutputStream;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.wm.SurfaceAnimator.Animatable;
@@ -95,6 +95,7 @@
protected final WindowManagerService mService;
private final Point mTmpPos = new Point();
+ protected final Point mLastSurfacePosition = new Point();
/** Total number of elements in this subtree, including our own hierarchy element. */
private int mTreeWeight = 1;
@@ -971,6 +972,7 @@
super.writeToProto(proto, CONFIGURATION_CONTAINER, trim);
proto.write(ORIENTATION, mOrientation);
proto.write(VISIBLE, isVisible());
+ mSurfaceAnimator.writeToProto(proto, SURFACE_ANIMATOR);
proto.end(token);
}
@@ -1178,7 +1180,12 @@
}
getRelativePosition(mTmpPos);
+ if (mTmpPos.equals(mLastSurfacePosition)) {
+ return;
+ }
+
transaction.setPosition(mSurfaceControl, mTmpPos.x, mTmpPos.y);
+ mLastSurfacePosition.set(mTmpPos.x, mTmpPos.y);
for (int i = mChildren.size() - 1; i >= 0; i--) {
mChildren.get(i).updateSurfacePosition(transaction);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c6eb12b..0fedc79 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.Manifest.permission.MANAGE_APP_TOKENS;
import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
@@ -87,7 +88,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
@@ -210,17 +210,18 @@
import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.PointerIcon;
+import android.view.RemoteAnimationAdapter;
import android.view.Surface;
import android.view.SurfaceControl;
-import android.view.SurfaceControl.Builder;
import android.view.SurfaceSession;
import android.view.View;
import android.view.WindowContentFrameStats;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
+import android.view.WindowManager.TransitionFlags;
+import android.view.WindowManager.TransitionType;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
-import android.view.animation.Animation;
import android.view.inputmethod.InputMethodManagerInternal;
import com.android.internal.R;
@@ -1540,7 +1541,7 @@
// We treat this as if this activity was opening, so we can trigger the app transition
// animation and piggy-back on existing transition animation infrastructure.
mOpeningApps.add(atoken);
- prepareAppTransition(AppTransition.TRANSIT_ACTIVITY_RELAUNCH, ALWAYS_KEEP_CURRENT);
+ prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_RELAUNCH, ALWAYS_KEEP_CURRENT);
mAppTransition.overridePendingAppTransitionClipReveal(frame.left, frame.top,
frame.width(), frame.height());
executeAppTransition();
@@ -1554,7 +1555,7 @@
// we don't set up the transition anymore and just let it go.
if (mDisplayFrozen && !mOpeningApps.contains(atoken) && atoken.isRelaunching()) {
mOpeningApps.add(atoken);
- prepareAppTransition(AppTransition.TRANSIT_NONE, !ALWAYS_KEEP_CURRENT);
+ prepareAppTransition(WindowManager.TRANSIT_NONE, !ALWAYS_KEEP_CURRENT);
executeAppTransition();
}
}
@@ -2509,7 +2510,7 @@
}
@Override
- public void prepareAppTransition(int transit, boolean alwaysKeepCurrent) {
+ public void prepareAppTransition(@TransitionType int transit, boolean alwaysKeepCurrent) {
prepareAppTransition(transit, alwaysKeepCurrent, 0 /* flags */, false /* forceOverride */);
}
@@ -2522,8 +2523,8 @@
* AppTransition.TRANSIT_FLAG_*.
* @param forceOverride Always override the transit, not matter what was set previously.
*/
- public void prepareAppTransition(int transit, boolean alwaysKeepCurrent, int flags,
- boolean forceOverride) {
+ public void prepareAppTransition(@TransitionType int transit, boolean alwaysKeepCurrent,
+ @TransitionFlags int flags, boolean forceOverride) {
if (!checkCallingPermission(MANAGE_APP_TOKENS, "prepareAppTransition()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
@@ -2539,7 +2540,7 @@
}
@Override
- public int getPendingAppTransition() {
+ public @TransitionType int getPendingAppTransition() {
return mAppTransition.getAppTransition();
}
@@ -2624,6 +2625,18 @@
}
@Override
+ public void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) {
+ if (!checkCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+ "overridePendingAppTransitionRemote()")) {
+ throw new SecurityException(
+ "Requires CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS permission");
+ }
+ synchronized (mWindowMap) {
+ mAppTransition.overridePendingAppTransitionRemote(remoteAnimationAdapter);
+ }
+ }
+
+ @Override
public void endProlongedAnimations() {
synchronized (mWindowMap) {
for (final WindowState win : mWindowMap.values()) {
@@ -2941,10 +2954,10 @@
}
@Override
- public void dismissKeyguard(IKeyguardDismissCallback callback) {
+ public void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message) {
checkCallingPermission(permission.CONTROL_KEYGUARD, "dismissKeyguard");
synchronized(mWindowMap) {
- mPolicy.dismissKeyguardLw(callback);
+ mPolicy.dismissKeyguardLw(callback, message);
}
}
@@ -5894,6 +5907,8 @@
* the screen is.
* @see WindowManagerPolicy#getNavBarPosition()
*/
+ @Override
+ @WindowManagerPolicy.NavigationBarPosition
public int getNavBarPosition() {
synchronized (mWindowMap) {
// Perform layout if it was scheduled before to make sure that we get correct nav bar
@@ -6595,6 +6610,14 @@
}
}
+ public void onOverlayChanged() {
+ synchronized (mWindowMap) {
+ mPolicy.onOverlayChangedLw();
+ mDisplayManagerInternal.onOverlayChanged();
+ requestTraversal();
+ }
+ }
+
public void onDisplayChanged(int displayId) {
synchronized (mWindowMap) {
final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index b9dc9db..e24c393 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -231,7 +231,7 @@
}
private int runDismissKeyguard(PrintWriter pw) throws RemoteException {
- mInterface.dismissKeyguard(null /* callback */);
+ mInterface.dismissKeyguard(null /* callback */, null /* message */);
return 0;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b3809dd..db30db0 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -20,6 +20,7 @@
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.SurfaceControl.Transaction;
+import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
@@ -30,6 +31,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
@@ -39,6 +41,8 @@
import static android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.FORMAT_CHANGED;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
@@ -189,6 +193,7 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.input.InputWindowHandle;
import com.android.server.policy.WindowManagerPolicy;
@@ -198,7 +203,6 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Comparator;
-import java.util.LinkedList;
import java.util.function.Predicate;
/** A window in the window manager. */
@@ -2977,7 +2981,33 @@
/** @return true when the window is in fullscreen task, but has non-fullscreen bounds set. */
boolean isLetterboxedAppWindow() {
- return !isInMultiWindowMode() && mAppToken != null && !mAppToken.matchParentBounds();
+ return !isInMultiWindowMode() && mAppToken != null && !mAppToken.matchParentBounds()
+ || isLetterboxedForDisplayCutoutLw();
+ }
+
+ @Override
+ public boolean isLetterboxedForDisplayCutoutLw() {
+ if (mAppToken == null) {
+ // Only windows with an AppWindowToken are letterboxed.
+ return false;
+ }
+ if (getDisplayContent().getDisplayInfo().displayCutout == null) {
+ // No cutout, no letterbox.
+ return false;
+ }
+ if (mAttrs.layoutInDisplayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
+ // Layout in cutout, no letterbox.
+ return false;
+ }
+ // TODO: handle dialogs and other non-filling windows
+ if (mAttrs.layoutInDisplayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER) {
+ // Never layout in cutout, always letterbox.
+ return true;
+ }
+ // Letterbox if any fullscreen mode is set.
+ final int fl = mAttrs.flags;
+ final int sysui = mSystemUiVisibility;
+ return (fl & FLAG_FULLSCREEN) != 0 || (sysui & (SYSTEM_UI_FLAG_FULLSCREEN)) != 0;
}
boolean isDragResizeChanged() {
@@ -3136,7 +3166,8 @@
proto.end(token);
}
- void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
+ @Override
+ public void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(HASH_CODE, System.identityHashCode(this));
proto.write(USER_ID, UserHandle.getUserId(mOwnerUid));
@@ -3922,6 +3953,22 @@
return null;
}
+ /**
+ * @return True if we our one of our ancestors has {@link #mAnimatingExit} set to true, false
+ * otherwise.
+ */
+ @VisibleForTesting
+ boolean isSelfOrAncestorWindowAnimatingExit() {
+ WindowState window = this;
+ do {
+ if (window.mAnimatingExit) {
+ return true;
+ }
+ window = window.getParentWindow();
+ } while (window != null);
+ return false;
+ }
+
void onExitAnimationDone() {
if (DEBUG_ANIM) Slog.v(TAG, "onExitAnimationDone in " + this
+ ": exiting=" + mAnimatingExit + " remove=" + mRemoveOnExit
@@ -3957,7 +4004,7 @@
mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
- if (!mAnimatingExit) {
+ if (!isSelfOrAncestorWindowAnimatingExit()) {
return;
}
@@ -4487,6 +4534,7 @@
// Leash is now responsible for position, so set our position to 0.
t.setPosition(mSurfaceControl, 0, 0);
+ mLastSurfacePosition.set(0, 0);
}
@Override
@@ -4502,8 +4550,9 @@
}
transformFrameToSurfacePosition(mFrame.left, mFrame.top, mSurfacePosition);
- if (!mSurfaceAnimator.hasLeash()) {
+ if (!mSurfaceAnimator.hasLeash() && !mLastSurfacePosition.equals(mSurfacePosition)) {
t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
+ mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 96b0bd5..ba5156b 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -41,7 +41,6 @@
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
import static com.android.server.wm.WindowManagerService.logWithStack;
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
-import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN;
import static com.android.server.wm.proto.WindowStateAnimatorProto.DRAW_STATE;
import static com.android.server.wm.proto.WindowStateAnimatorProto.LAST_CLIP_RECT;
import static com.android.server.wm.proto.WindowStateAnimatorProto.SURFACE;
@@ -1165,7 +1164,7 @@
// potentially occurring while turning off the screen. This would lead to the
// screen incorrectly turning back on.
if (!mService.mPowerManager.isInteractive()) {
- mAnimator.mBulkUpdateParams |= SET_TURN_ON_SCREEN;
+ mService.mTurnOnScreen = true;
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index e26a362..2f38556 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -53,7 +53,7 @@
final WindowStateAnimator mAnimator;
- SurfaceControlWithBackground mSurfaceControl;
+ SurfaceControl mSurfaceControl;
// Should only be set from within setShown().
private boolean mSurfaceShown = false;
@@ -108,13 +108,11 @@
.setFormat(format)
.setFlags(flags)
.setMetadata(windowType, ownerUid);
- mSurfaceControl = new SurfaceControlWithBackground(
- name, b, windowType, w, h, this);
+ mSurfaceControl = b.build();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (mService.mRoot.mSurfaceTraceEnabled) {
- mSurfaceControl = new RemoteSurfaceTrace(
- mService.mRoot.mSurfaceTraceFd.getFileDescriptor(), mSurfaceControl, win);
+ installRemoteTrace(mService.mRoot.mSurfaceTraceFd.getFileDescriptor());
}
}
@@ -123,7 +121,7 @@
}
void removeRemoteTrace() {
- mSurfaceControl = new SurfaceControlWithBackground(mSurfaceControl);
+ mSurfaceControl = new SurfaceControl(mSurfaceControl);
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index a512fdf..7364e87 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -23,23 +23,23 @@
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
-import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_CLOSE;
-import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
-import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
-import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
-import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
-import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_GOING_AWAY;
-import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
-import static com.android.server.wm.AppTransition.TRANSIT_NONE;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_CLOSE;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_IN_PLACE;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_BACK;
-import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT;
-import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_CLOSE;
-import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_INTRA_CLOSE;
-import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN;
-import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_OPEN;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_WALLPAPER_CLOSE;
+import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE;
+import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN;
+import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
@@ -57,14 +57,19 @@
import android.util.Slog;
import android.util.SparseIntArray;
import android.view.Display;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationDefinition;
import android.view.SurfaceControl;
+import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
+import android.view.WindowManager.TransitionType;
import android.view.animation.Animation;
import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.function.Predicate;
/**
* Positions windows and their surfaces.
@@ -86,8 +91,7 @@
static final int SET_WALLPAPER_MAY_CHANGE = 1 << 1;
static final int SET_FORCE_HIDING_CHANGED = 1 << 2;
static final int SET_ORIENTATION_CHANGE_COMPLETE = 1 << 3;
- static final int SET_TURN_ON_SCREEN = 1 << 4;
- static final int SET_WALLPAPER_ACTION_PENDING = 1 << 5;
+ static final int SET_WALLPAPER_ACTION_PENDING = 1 << 4;
private boolean mTraversalScheduled;
private int mDeferDepth = 0;
@@ -248,7 +252,7 @@
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO");
int transit = mService.mAppTransition.getAppTransition();
if (mService.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) {
- transit = AppTransition.TRANSIT_UNSET;
+ transit = WindowManager.TRANSIT_UNSET;
}
mService.mSkipAppTransitionAnimation = false;
mService.mNoAnimationNotifyOnTransitionFinished.clear();
@@ -256,17 +260,9 @@
mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
final DisplayContent displayContent = mService.getDefaultDisplayContentLocked();
- // TODO: Don't believe this is really needed...
- //mService.mWindowsChanged = true;
mService.mRoot.mWallpaperMayChange = false;
- // The top-most window will supply the layout params, and we will determine it below.
- LayoutParams animLp = null;
- int bestAnimLayer = -1;
- boolean fullscreenAnim = false;
- boolean voiceInteraction = false;
-
int i;
for (i = 0; i < appsCount; i++) {
final AppWindowToken wtoken = mService.mOpeningApps.valueAt(i);
@@ -275,7 +271,6 @@
// visibility. We need to clear it *before* maybeUpdateTransitToWallpaper() as the
// transition selection depends on wallpaper target visibility.
wtoken.clearAnimatingFlags();
-
}
// Adjust wallpaper before we pull the lower/upper target, since pending changes
@@ -284,62 +279,30 @@
mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(displayContent,
mService.mOpeningApps);
- final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
- boolean openingAppHasWallpaper = false;
- boolean closingAppHasWallpaper = false;
-
- // Do a first pass through the tokens for two things:
- // (1) Determine if both the closing and opening app token sets are wallpaper targets, in
- // which case special animations are needed (since the wallpaper needs to stay static behind
- // them).
- // (2) Find the layout params of the top-most application window in the tokens, which is
- // what will control the animation theme.
- final int closingAppsCount = mService.mClosingApps.size();
- appsCount = closingAppsCount + mService.mOpeningApps.size();
- for (i = 0; i < appsCount; i++) {
- final AppWindowToken wtoken;
- if (i < closingAppsCount) {
- wtoken = mService.mClosingApps.valueAt(i);
- if (wallpaperTarget != null && wtoken.windowsCanBeWallpaperTarget()) {
- closingAppHasWallpaper = true;
- }
- } else {
- wtoken = mService.mOpeningApps.valueAt(i - closingAppsCount);
- if (wallpaperTarget != null && wtoken.windowsCanBeWallpaperTarget()) {
- openingAppHasWallpaper = true;
- }
- }
-
- voiceInteraction |= wtoken.mVoiceInteraction;
-
- if (wtoken.fillsParent()) {
- final WindowState ws = wtoken.findMainWindow();
- if (ws != null) {
- animLp = ws.mAttrs;
- bestAnimLayer = ws.mLayer;
- fullscreenAnim = true;
- }
- } else if (!fullscreenAnim) {
- final WindowState ws = wtoken.findMainWindow();
- if (ws != null) {
- if (ws.mLayer > bestAnimLayer) {
- animLp = ws.mAttrs;
- bestAnimLayer = ws.mLayer;
- }
- }
- }
- }
+ // Determine if closing and opening app token sets are wallpaper targets, in which case
+ // special animations are needed.
+ final boolean hasWallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget() != null;
+ final boolean openingAppHasWallpaper = canBeWallpaperTarget(mService.mOpeningApps)
+ && hasWallpaperTarget;
+ final boolean closingAppHasWallpaper = canBeWallpaperTarget(mService.mClosingApps)
+ && hasWallpaperTarget;
transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper,
closingAppHasWallpaper);
- // If all closing windows are obscured, then there is no need to do an animation. This is
- // the case, for example, when this transition is being done behind the lock screen.
- if (!mService.mPolicy.allowAppAnimationsLw()) {
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
- "Animations disallowed by keyguard or dream.");
- animLp = null;
- }
+ // Find the layout params of the top-most application window in the tokens, which is
+ // what will control the animation theme. If all closing windows are obscured, then there is
+ // no need to do an animation. This is the case, for example, when this transition is being
+ // done behind a dream window.
+ final AppWindowToken animLpToken = mService.mPolicy.allowAppAnimationsLw()
+ ? findAnimLayoutParamsToken(transit)
+ : null;
+
+ final LayoutParams animLp = getAnimLp(animLpToken);
+ overrideWithRemoteAnimationIfSet(animLpToken, transit);
+
+ final boolean voiceInteraction = containsVoiceInteraction(mService.mOpeningApps)
+ || containsVoiceInteraction(mService.mOpeningApps);
final int layoutRedo;
mService.mSurfaceAnimationRunner.deferStartingAnimations();
@@ -389,6 +352,81 @@
return layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG;
}
+ private static LayoutParams getAnimLp(AppWindowToken wtoken) {
+ final WindowState mainWindow = wtoken != null ? wtoken.findMainWindow() : null;
+ return mainWindow != null ? mainWindow.mAttrs : null;
+ }
+
+ /**
+ * Overrides the pending transition with the remote animation defined for the transition in the
+ * set of defined remote animations in the app window token.
+ */
+ private void overrideWithRemoteAnimationIfSet(AppWindowToken animLpToken, int transit) {
+ if (animLpToken == null) {
+ return;
+ }
+ final RemoteAnimationDefinition definition = animLpToken.getRemoteAnimationDefinition();
+ if (definition != null) {
+ final RemoteAnimationAdapter adapter = definition.getAdapter(transit);
+ if (adapter != null) {
+ mService.mAppTransition.overridePendingAppTransitionRemote(adapter);
+ }
+ }
+ }
+
+ /**
+ * @return The window token that determines the animation theme.
+ */
+ private AppWindowToken findAnimLayoutParamsToken(@TransitionType int transit) {
+ AppWindowToken result;
+
+ // Remote animations always win, but fullscreen tokens override non-fullscreen tokens.
+ result = lookForHighestTokenWithFilter(mService.mClosingApps, mService.mOpeningApps,
+ w -> w.getRemoteAnimationDefinition() != null
+ && w.getRemoteAnimationDefinition().hasTransition(transit));
+ if (result != null) {
+ return result;
+ }
+ result = lookForHighestTokenWithFilter(mService.mClosingApps, mService.mOpeningApps,
+ w -> w.fillsParent() && w.findMainWindow() != null);
+ if (result != null) {
+ return result;
+ }
+ return lookForHighestTokenWithFilter(mService.mClosingApps, mService.mOpeningApps,
+ w -> w.findMainWindow() != null);
+ }
+
+ private AppWindowToken lookForHighestTokenWithFilter(ArraySet<AppWindowToken> array1,
+ ArraySet<AppWindowToken> array2, Predicate<AppWindowToken> filter) {
+ final int array1count = array1.size();
+ final int count = array1count + array2.size();
+ int bestPrefixOrderIndex = Integer.MIN_VALUE;
+ AppWindowToken bestToken = null;
+ for (int i = 0; i < count; i++) {
+ final AppWindowToken wtoken;
+ if (i < array1count) {
+ wtoken = array1.valueAt(i);
+ } else {
+ wtoken = array2.valueAt(i - array1count);
+ }
+ final int prefixOrderIndex = wtoken.getPrefixOrderIndex();
+ if (filter.test(wtoken) && prefixOrderIndex > bestPrefixOrderIndex) {
+ bestPrefixOrderIndex = prefixOrderIndex;
+ bestToken = wtoken;
+ }
+ }
+ return bestToken;
+ }
+
+ private boolean containsVoiceInteraction(ArraySet<AppWindowToken> apps) {
+ for (int i = apps.size() - 1; i >= 0; i--) {
+ if (apps.valueAt(i).mVoiceInteraction) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private AppWindowToken handleOpeningApps(int transit, LayoutParams animLp,
boolean voiceInteraction) {
AppWindowToken topOpeningApp = null;
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index bad9bf5..172efdc 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -16,11 +16,7 @@
package com.android.server.wm;
-import android.annotation.CallSuper;
-import android.util.proto.ProtoOutputStream;
-import java.util.Comparator;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
@@ -28,15 +24,20 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.proto.WindowTokenProto.HASH_CODE;
+import static com.android.server.wm.proto.WindowTokenProto.HIDDEN;
+import static com.android.server.wm.proto.WindowTokenProto.PAUSED;
+import static com.android.server.wm.proto.WindowTokenProto.WAITING_TO_SHOW;
import static com.android.server.wm.proto.WindowTokenProto.WINDOWS;
import static com.android.server.wm.proto.WindowTokenProto.WINDOW_CONTAINER;
+import android.annotation.CallSuper;
import android.os.Debug;
import android.os.IBinder;
import android.util.Slog;
-import android.view.SurfaceControl;
+import android.util.proto.ProtoOutputStream;
import java.io.PrintWriter;
+import java.util.Comparator;
/**
* Container of a set of related windows in the window manager. Often this is an AppWindowToken,
@@ -276,6 +277,9 @@
final WindowState w = mChildren.get(i);
w.writeToProto(proto, WINDOWS, trim);
}
+ proto.write(HIDDEN, mHidden);
+ proto.write(WAITING_TO_SHOW, waitingToShow);
+ proto.write(PAUSED, paused);
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/utils/CoordinateTransforms.java b/services/core/java/com/android/server/wm/utils/CoordinateTransforms.java
new file mode 100644
index 0000000..09d7b5d
--- /dev/null
+++ b/services/core/java/com/android/server/wm/utils/CoordinateTransforms.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.utils;
+
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import android.annotation.Dimension;
+import android.graphics.Matrix;
+import android.view.Surface.Rotation;
+
+public class CoordinateTransforms {
+
+ private CoordinateTransforms() {
+ }
+
+ /**
+ * Sets a matrix such that given a rotation, it transforms physical display
+ * coordinates to that rotation's logical coordinates.
+ *
+ * @param rotation the rotation to which the matrix should transform
+ * @param out the matrix to be set
+ */
+ public static void transformPhysicalToLogicalCoordinates(@Rotation int rotation,
+ @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) {
+ switch (rotation) {
+ case ROTATION_0:
+ out.reset();
+ break;
+ case ROTATION_90:
+ out.setRotate(270);
+ out.postTranslate(0, physicalWidth);
+ break;
+ case ROTATION_180:
+ out.setRotate(180);
+ out.postTranslate(physicalWidth, physicalHeight);
+ break;
+ case ROTATION_270:
+ out.setRotate(90);
+ out.postTranslate(physicalHeight, 0);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown rotation: " + rotation);
+ }
+ }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index c1e95eb..384b416 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -19,6 +19,7 @@
import android.app.admin.IDevicePolicyManager;
import android.content.ComponentName;
import android.os.PersistableBundle;
+import android.os.UserHandle;
import android.security.keymaster.KeymasterCertificateChain;
import android.security.keystore.ParcelableKeyGenParameterSpec;
@@ -38,6 +39,12 @@
*/
abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
/**
+ * To be called by {@link DevicePolicyManagerService#Lifecycle} when the service is started.
+ *
+ * @see {@link SystemService#onStart}.
+ */
+ abstract void handleStart();
+ /**
* To be called by {@link DevicePolicyManagerService#Lifecycle} during the various boot phases.
*
* @see {@link SystemService#onBootPhase}.
@@ -64,7 +71,7 @@
public void setSystemSetting(ComponentName who, String setting, String value){}
- public void transferOwner(ComponentName admin, ComponentName target, PersistableBundle bundle) {}
+ public void transferOwnership(ComponentName admin, ComponentName target, PersistableBundle bundle) {}
public boolean generateKeyPair(ComponentName who, String callerPackage, String algorithm,
ParcelableKeyGenParameterSpec keySpec, int idAttestationFlags,
@@ -97,4 +104,26 @@
byte[] cert, byte[] chain, boolean isUserSelectable) {
return false;
}
+
+ @Override
+ public boolean startUserInBackground(ComponentName who, UserHandle userHandle) {
+ return false;
+ }
+
+ @Override
+ public void setStartUserSessionMessage(
+ ComponentName admin, CharSequence startUserSessionMessage) {}
+
+ @Override
+ public void setEndUserSessionMessage(ComponentName admin, CharSequence endUserSessionMessage) {}
+
+ @Override
+ public String getStartUserSessionMessage(ComponentName admin) {
+ return null;
+ }
+
+ @Override
+ public String getEndUserSessionMessage(ComponentName admin) {
+ return null;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f0681e9..cb4f5c1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -20,6 +20,7 @@
import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.USER_OP_SUCCESS;
+import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
import static android.app.admin.DevicePolicyManager.CODE_ACCOUNTS_NOT_EMPTY;
import static android.app.admin.DevicePolicyManager.CODE_ADD_MANAGED_PROFILE_DISALLOWED;
@@ -53,7 +54,6 @@
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
-import static android.app.admin.DevicePolicyManager.START_USER_IN_BACKGROUND;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
@@ -95,6 +95,7 @@
import android.app.admin.SystemUpdatePolicy;
import android.app.backup.IBackupManager;
import android.app.trust.TrustManager;
+import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -149,12 +150,12 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
+import android.os.UserManagerInternal.UserRestrictionsListener;
import android.os.storage.StorageManager;
import android.provider.ContactsContract.QuickContact;
import android.provider.ContactsInternal;
import android.provider.Settings;
import android.provider.Settings.Global;
-import android.security.Credentials;
import android.security.IKeyChainAliasCallback;
import android.security.IKeyChainService;
import android.security.KeyChain;
@@ -199,6 +200,7 @@
import com.android.server.SystemService;
import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
import com.android.server.pm.UserRestrictionsUtils;
+
import com.google.android.collect.Sets;
import org.xmlpull.v1.XmlPullParser;
@@ -225,6 +227,7 @@
import java.util.List;
import java.util.Map.Entry;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -401,6 +404,7 @@
final IPackageManager mIPackageManager;
final UserManager mUserManager;
final UserManagerInternal mUserManagerInternal;
+ final UsageStatsManagerInternal mUsageStatsManagerInternal;
final TelephonyManager mTelephonyManager;
private final LockPatternUtils mLockPatternUtils;
private final DevicePolicyConstants mConstants;
@@ -504,6 +508,7 @@
@Override
public void onStart() {
publishBinderService(Context.DEVICE_POLICY_SERVICE, mService);
+ mService.handleStart();
}
@Override
@@ -700,6 +705,33 @@
}
};
+ protected static class RestrictionsListener implements UserRestrictionsListener {
+ private Context mContext;
+
+ public RestrictionsListener(Context context) {
+ mContext = context;
+ }
+
+ public void onUserRestrictionsChanged(int userId, Bundle newRestrictions,
+ Bundle prevRestrictions) {
+ final boolean newlyDisallowed =
+ newRestrictions.getBoolean(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE);
+ final boolean previouslyDisallowed =
+ prevRestrictions.getBoolean(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE);
+ final boolean restrictionChanged = (newlyDisallowed != previouslyDisallowed);
+
+ if (restrictionChanged) {
+ // Notify ManagedProvisioning to update the built-in cross profile intent filters.
+ Intent intent = new Intent(
+ DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_CHANGED);
+ intent.setPackage(MANAGED_PROVISIONING_PKG);
+ intent.putExtra(Intent.EXTRA_USER_ID, userId);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+ }
+ }
+ }
+
static class ActiveAdmin {
private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
private static final String TAG_TEST_ONLY_ADMIN = "test-only-admin";
@@ -760,6 +792,9 @@
private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
private static final String TAG_IS_LOGOUT_ENABLED = "is_logout_enabled";
+ private static final String TAG_MANDATORY_BACKUP_TRANSPORT = "mandatory_backup_transport";
+ private static final String TAG_START_USER_SESSION_MESSAGE = "start_user_session_message";
+ private static final String TAG_END_USER_SESSION_MESSAGE = "end_user_session_message";
DeviceAdminInfo info;
@@ -876,6 +911,14 @@
// The blacklist data is stored in a file whose name is stored in the XML
String passwordBlacklistFile = null;
+ // The component name of the backup transport which has to be used if backups are mandatory
+ // or null if backups are not mandatory.
+ ComponentName mandatoryBackupTransport = null;
+
+ // Message for user switcher
+ String startUserSessionMessage = null;
+ String endUserSessionMessage = null;
+
ActiveAdmin(DeviceAdminInfo _info, boolean parent) {
info = _info;
isParent = parent;
@@ -1139,6 +1182,21 @@
out.attribute(null, ATTR_VALUE, Boolean.toString(isLogoutEnabled));
out.endTag(null, TAG_IS_LOGOUT_ENABLED);
}
+ if (mandatoryBackupTransport != null) {
+ out.startTag(null, TAG_MANDATORY_BACKUP_TRANSPORT);
+ out.attribute(null, ATTR_VALUE, mandatoryBackupTransport.flattenToString());
+ out.endTag(null, TAG_MANDATORY_BACKUP_TRANSPORT);
+ }
+ if (startUserSessionMessage != null) {
+ out.startTag(null, TAG_START_USER_SESSION_MESSAGE);
+ out.text(startUserSessionMessage);
+ out.endTag(null, TAG_START_USER_SESSION_MESSAGE);
+ }
+ if (endUserSessionMessage != null) {
+ out.startTag(null, TAG_END_USER_SESSION_MESSAGE);
+ out.text(endUserSessionMessage);
+ out.endTag(null, TAG_END_USER_SESSION_MESSAGE);
+ }
}
void writePackageListToXml(XmlSerializer out, String outerTag,
@@ -1317,6 +1375,23 @@
} else if (TAG_IS_LOGOUT_ENABLED.equals(tag)) {
isLogoutEnabled = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_VALUE));
+ } else if (TAG_MANDATORY_BACKUP_TRANSPORT.equals(tag)) {
+ mandatoryBackupTransport = ComponentName.unflattenFromString(
+ parser.getAttributeValue(null, ATTR_VALUE));
+ } else if (TAG_START_USER_SESSION_MESSAGE.equals(tag)) {
+ type = parser.next();
+ if (type == XmlPullParser.TEXT) {
+ startUserSessionMessage = parser.getText();
+ } else {
+ Log.w(LOG_TAG, "Missing text when loading start session message");
+ }
+ } else if (TAG_END_USER_SESSION_MESSAGE.equals(tag)) {
+ type = parser.next();
+ if (type == XmlPullParser.TEXT) {
+ endUserSessionMessage = parser.getText();
+ } else {
+ Log.w(LOG_TAG, "Missing text when loading end session message");
+ }
} else {
Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -1560,6 +1635,7 @@
removedAdmin = true;
policy.mAdminList.remove(i);
policy.mAdminMap.remove(aa.info.getComponent());
+ pushActiveAdminPackagesLocked(userHandle);
}
}
} catch (RemoteException re) {
@@ -1653,6 +1729,10 @@
return LocalServices.getService(PackageManagerInternal.class);
}
+ UsageStatsManagerInternal getUsageStatsManagerInternal() {
+ return LocalServices.getService(UsageStatsManagerInternal.class);
+ }
+
NotificationManager getNotificationManager() {
return mContext.getSystemService(NotificationManager.class);
}
@@ -1691,6 +1771,10 @@
return ActivityManager.getService();
}
+ ActivityManagerInternal getActivityManagerInternal() {
+ return LocalServices.getService(ActivityManagerInternal.class);
+ }
+
IPackageManager getIPackageManager() {
return AppGlobals.getPackageManager();
}
@@ -1923,6 +2007,8 @@
mUserManager = Preconditions.checkNotNull(injector.getUserManager());
mUserManagerInternal = Preconditions.checkNotNull(injector.getUserManagerInternal());
+ mUsageStatsManagerInternal = Preconditions.checkNotNull(
+ injector.getUsageStatsManagerInternal());
mIPackageManager = Preconditions.checkNotNull(injector.getIPackageManager());
mTelephonyManager = Preconditions.checkNotNull(injector.getTelephonyManager());
@@ -1972,6 +2058,8 @@
LocalServices.addService(DevicePolicyManagerInternal.class, mLocalService);
mSetupContentObserver = new SetupContentObserver(mHandler);
+
+ mUserManagerInternal.addUserRestrictionsListener(new RestrictionsListener(mContext));
}
/**
@@ -3158,10 +3246,18 @@
}
synchronized (this) {
- // push the force-ephemeral-users policy to the user manager.
ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
if (deviceOwner != null) {
+ // Push the force-ephemeral-users policy to the user manager.
mUserManagerInternal.setForceEphemeralUsers(deviceOwner.forceEphemeralUsers);
+
+ // Update user switcher message to activity manager.
+ ActivityManagerInternal activityManagerInternal =
+ mInjector.getActivityManagerInternal();
+ activityManagerInternal.setSwitchingFromSystemUserMessage(
+ deviceOwner.startUserSessionMessage);
+ activityManagerInternal.setSwitchingToSystemUserMessage(
+ deviceOwner.endUserSessionMessage);
}
}
}
@@ -3190,6 +3286,11 @@
}
@Override
+ void handleStart() {
+ pushActiveAdminPackages();
+ }
+
+ @Override
void handleStartUser(int userId) {
updateScreenCaptureDisabledInWindowManager(userId,
getScreenCaptureDisabled(null, userId));
@@ -3357,6 +3458,8 @@
if (replaceIndex == -1) {
policy.mAdminList.add(newAdmin);
enableIfNecessary(info.getPackageName(), userHandle);
+ mUsageStatsManagerInternal.onActiveAdminAdded(
+ adminReceiver.getPackageName(), userHandle);
} else {
policy.mAdminList.set(replaceIndex, newAdmin);
}
@@ -3369,6 +3472,35 @@
}
}
+ private void pushActiveAdminPackages() {
+ synchronized (this) {
+ final List<UserInfo> users = mUserManager.getUsers();
+ for (int i = users.size() - 1; i >= 0; --i) {
+ final int userId = users.get(i).id;
+ mUsageStatsManagerInternal.setActiveAdminApps(
+ getActiveAdminPackagesLocked(userId), userId);
+ }
+ }
+ }
+
+ private void pushActiveAdminPackagesLocked(int userId) {
+ mUsageStatsManagerInternal.setActiveAdminApps(
+ getActiveAdminPackagesLocked(userId), userId);
+ }
+
+ private Set<String> getActiveAdminPackagesLocked(int userId) {
+ final DevicePolicyData policy = getUserData(userId);
+ Set<String> adminPkgs = null;
+ for (int i = policy.mAdminList.size() - 1; i >= 0; --i) {
+ final String pkgName = policy.mAdminList.get(i).info.getPackageName();
+ if (adminPkgs == null) {
+ adminPkgs = new ArraySet<>();
+ }
+ adminPkgs.add(pkgName);
+ }
+ return adminPkgs;
+ }
+
private void transferActiveAdminUncheckedLocked(ComponentName incomingReceiver,
ComponentName outgoingReceiver, int userHandle) {
final DevicePolicyData policy = getUserData(userHandle);
@@ -3487,6 +3619,7 @@
}
}
+ @Override
public void forceRemoveActiveAdmin(ComponentName adminReceiver, int userHandle) {
if (!mHasFeature) {
return;
@@ -3540,7 +3673,7 @@
private boolean isPackageTestOnly(String packageName, int userHandle) {
final ApplicationInfo ai;
try {
- ai = mIPackageManager.getApplicationInfo(packageName,
+ ai = mInjector.getIPackageManager().getApplicationInfo(packageName,
(PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE), userHandle);
} catch (RemoteException e) {
@@ -3562,7 +3695,7 @@
}
private void enforceShell(String method) {
- final int callingUid = Binder.getCallingUid();
+ final int callingUid = mInjector.binderGetCallingUid();
if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID) {
throw new SecurityException("Non-shell user attempted to call " + method);
}
@@ -4423,6 +4556,28 @@
}
}
+ private boolean canPOorDOCallResetPassword(ActiveAdmin admin, @UserIdInt int userId) {
+ // Only if the admins targets a pre-O SDK
+ return getTargetSdk(admin.info.getPackageName(), userId) < Build.VERSION_CODES.O;
+ }
+
+ /* PO or DO could do an untrusted reset in certain conditions. */
+ private boolean canUserHaveUntrustedCredentialReset(@UserIdInt int userId) {
+ synchronized (this) {
+ // An active DO or PO might be able to fo an untrusted credential reset
+ for (final ActiveAdmin admin : getUserData(userId).mAdminList) {
+ if (!isActiveAdminWithPolicyForUserLocked(admin,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, userId)) {
+ continue;
+ }
+ if (canPOorDOCallResetPassword(admin, userId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
@Override
public boolean resetPassword(String passwordOrNull, int flags) throws RemoteException {
final int callingUid = mInjector.binderGetCallingUid();
@@ -4441,12 +4596,12 @@
null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, callingUid);
final boolean preN;
if (admin != null) {
- final int targetSdk = getTargetSdk(admin.info.getPackageName(), userHandle);
- if (targetSdk >= Build.VERSION_CODES.O) {
+ if (!canPOorDOCallResetPassword(admin, userHandle)) {
throw new SecurityException("resetPassword() is deprecated for DPC targeting O"
+ " or later");
}
- preN = targetSdk <= android.os.Build.VERSION_CODES.M;
+ preN = getTargetSdk(admin.info.getPackageName(),
+ userHandle) <= android.os.Build.VERSION_CODES.M;
} else {
// Otherwise, make sure the caller has any active admin with the right policy.
admin = getActiveAdminForCallerLocked(null,
@@ -6506,13 +6661,36 @@
}
}
- synchronized void sendDeviceOwnerCommand(String action, Bundle extras) {
- Intent intent = new Intent(action);
- intent.setComponent(mOwners.getDeviceOwnerComponent());
+ void sendDeviceOwnerCommand(String action, Bundle extras) {
+ int deviceOwnerUserId;
+ ComponentName deviceOwnerComponent;
+ synchronized (this) {
+ deviceOwnerUserId = mOwners.getDeviceOwnerUserId();
+ deviceOwnerComponent = mOwners.getDeviceOwnerComponent();
+ }
+ sendActiveAdminCommand(action, extras, deviceOwnerUserId,
+ deviceOwnerComponent);
+ }
+
+ private void sendProfileOwnerCommand(String action, Bundle extras, int userHandle) {
+ sendActiveAdminCommand(action, extras, userHandle,
+ mOwners.getProfileOwnerComponent(userHandle));
+ }
+
+ private void sendActiveAdminCommand(String action, Bundle extras,
+ int userHandle, ComponentName receiverComponent) {
+ final Intent intent = new Intent(action);
+ intent.setComponent(receiverComponent);
if (extras != null) {
intent.putExtras(extras);
}
- mContext.sendBroadcastAsUser(intent, UserHandle.of(mOwners.getDeviceOwnerUserId()));
+ mContext.sendBroadcastAsUser(intent, UserHandle.of(userHandle));
+ }
+
+ private void sendOwnerChangedBroadcast(String broadcast, int userId) {
+ final Intent intent = new Intent(broadcast)
+ .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
}
private synchronized String getDeviceOwnerRemoteBugreportUri() {
@@ -6890,10 +7068,7 @@
ident = mInjector.binderClearCallingIdentity();
try {
// TODO Send to system too?
- mContext.sendBroadcastAsUser(
- new Intent(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED)
- .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND),
- UserHandle.of(userId));
+ sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED, userId);
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
@@ -7044,9 +7219,8 @@
try {
clearDeviceOwnerLocked(admin, deviceOwnerUserId);
removeActiveAdminLocked(deviceOwnerComponent, deviceOwnerUserId);
- Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- mContext.sendBroadcastAsUser(intent, UserHandle.of(deviceOwnerUserId));
+ sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED,
+ deviceOwnerUserId);
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
@@ -7131,6 +7305,8 @@
ensureUnknownSourcesRestrictionForProfileOwnerLocked(userHandle, admin,
true /* newOwner */);
}
+ sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED,
+ userHandle);
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -7159,6 +7335,8 @@
try {
clearProfileOwnerLocked(admin, userId);
removeActiveAdminLocked(who, userId);
+ sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED,
+ userId);
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
@@ -7216,6 +7394,7 @@
policy.mUserProvisioningState = DevicePolicyManager.STATE_USER_UNMANAGED;
policy.mAffiliationIds.clear();
policy.mLockTaskPackages.clear();
+ updateLockTaskPackagesLocked(policy.mLockTaskPackages, userId);
policy.mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
saveSettingsLocked(userId);
@@ -8541,14 +8720,6 @@
Settings.Secure.USER_SETUP_COMPLETE, 1, userHandle);
}
- if ((flags & START_USER_IN_BACKGROUND) != 0) {
- try {
- mInjector.getIActivityManager().startUserInBackground(userHandle);
- } catch (RemoteException re) {
- // Does not happen, same process
- }
- }
-
return user;
} catch (Throwable re) {
mUserManager.removeUser(userHandle);
@@ -8561,6 +8732,8 @@
@Override
public boolean removeUser(ComponentName who, UserHandle userHandle) {
Preconditions.checkNotNull(who, "ComponentName is null");
+ Preconditions.checkNotNull(userHandle, "UserHandle is null");
+
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
}
@@ -8599,6 +8772,7 @@
@Override
public boolean switchUser(ComponentName who, UserHandle userHandle) {
Preconditions.checkNotNull(who, "ComponentName is null");
+
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
@@ -8619,8 +8793,40 @@
}
@Override
+ public boolean startUserInBackground(ComponentName who, UserHandle userHandle) {
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ Preconditions.checkNotNull(userHandle, "UserHandle is null");
+
+ synchronized (this) {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ }
+
+ final int userId = userHandle.getIdentifier();
+ if (isManagedProfile(userId)) {
+ Log.w(LOG_TAG, "Managed profile cannot be started in background");
+ return false;
+ }
+
+ final long id = mInjector.binderClearCallingIdentity();
+ try {
+ if (!mInjector.getActivityManagerInternal().canStartMoreUsers()) {
+ Log.w(LOG_TAG, "Cannot start more users in background");
+ return false;
+ }
+
+ return mInjector.getIActivityManager().startUserInBackground(userId);
+ } catch (RemoteException e) {
+ // Same process, should not happen.
+ return false;
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+ }
+
+ @Override
public boolean stopUser(ComponentName who, UserHandle userHandle) {
Preconditions.checkNotNull(who, "ComponentName is null");
+ Preconditions.checkNotNull(userHandle, "UserHandle is null");
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
@@ -9669,7 +9875,7 @@
return false;
}
mLockPatternUtils.setLockScreenDisabled(disabled, userId);
- mInjector.getIWindowManager().dismissKeyguard(null);
+ mInjector.getIWindowManager().dismissKeyguard(null /* callback */, null /* message */);
} catch (RemoteException e) {
// Same process, does not happen.
} finally {
@@ -9966,6 +10172,11 @@
updateMaximumTimeToLockLocked(userId);
}
}
+
+ @Override
+ public boolean canUserHaveUntrustedCredentialReset(@UserIdInt int userId) {
+ return DevicePolicyManagerService.this.canUserHaveUntrustedCredentialReset(userId);
+ }
}
private Intent createShowAdminSupportIntent(ComponentName admin, int userId) {
@@ -11140,6 +11351,7 @@
if (doProxyCleanup) {
resetGlobalProxyLocked(policy);
}
+ pushActiveAdminPackagesLocked(userHandle);
saveSettingsLocked(userHandle);
updateMaximumTimeToLockLocked(userHandle);
policy.mRemovingAdmins.remove(adminReceiver);
@@ -11206,7 +11418,12 @@
}
Preconditions.checkNotNull(admin);
synchronized (this) {
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(
+ admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ if (!enabled) {
+ activeAdmin.mandatoryBackupTransport = null;
+ saveSettingsLocked(UserHandle.USER_SYSTEM);
+ }
}
final long ident = mInjector.binderClearCallingIdentity();
@@ -11241,6 +11458,50 @@
}
@Override
+ public void setMandatoryBackupTransport(
+ ComponentName admin, ComponentName backupTransportComponent) {
+ if (!mHasFeature) {
+ return;
+ }
+ Preconditions.checkNotNull(admin);
+ synchronized (this) {
+ ActiveAdmin activeAdmin =
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ if (!Objects.equals(backupTransportComponent, activeAdmin.mandatoryBackupTransport)) {
+ activeAdmin.mandatoryBackupTransport = backupTransportComponent;
+ saveSettingsLocked(UserHandle.USER_SYSTEM);
+ }
+ }
+ final long identity = mInjector.binderClearCallingIdentity();
+ try {
+ IBackupManager ibm = mInjector.getIBackupManager();
+ if (ibm != null && backupTransportComponent != null) {
+ if (!ibm.isBackupServiceActive(UserHandle.USER_SYSTEM)) {
+ ibm.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ }
+ ibm.selectBackupTransportAsync(backupTransportComponent, null);
+ ibm.setBackupEnabled(true);
+ }
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Failed to set mandatory backup transport.", e);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public ComponentName getMandatoryBackupTransport() {
+ if (!mHasFeature) {
+ return null;
+ }
+ synchronized (this) {
+ ActiveAdmin activeAdmin = getDeviceOwnerAdminLocked();
+ return activeAdmin == null ? null : activeAdmin.mandatoryBackupTransport;
+ }
+ }
+
+
+ @Override
public boolean bindDeviceAdminServiceAsUser(
@NonNull ComponentName admin, @NonNull IApplicationThread caller,
@Nullable IBinder activtiyToken, @NonNull Intent serviceIntent,
@@ -11812,15 +12073,18 @@
return;
}
Preconditions.checkNotNull(admin);
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- if (enabled == isLogoutEnabledInternalLocked()) {
- // already in the requested state
- return;
+ synchronized (this) {
+ ActiveAdmin deviceOwner =
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+ if (deviceOwner.isLogoutEnabled == enabled) {
+ // already in the requested state
+ return;
+ }
+ deviceOwner.isLogoutEnabled = enabled;
+ saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
- ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
- deviceOwner.isLogoutEnabled = enabled;
- saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
@Override
@@ -11829,15 +12093,11 @@
return false;
}
synchronized (this) {
- return isLogoutEnabledInternalLocked();
+ ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ return (deviceOwner != null) && deviceOwner.isLogoutEnabled;
}
}
- private boolean isLogoutEnabledInternalLocked() {
- ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
- return (deviceOwner != null) && deviceOwner.isLogoutEnabled;
- }
-
@Override
public List<String> getDisallowedSystemApps(ComponentName admin, int userId,
String provisioningAction) throws RemoteException {
@@ -11847,9 +12107,9 @@
}
//TODO: Add callback information to the javadoc once it is completed.
- //TODO: Make transferOwner atomic.
+ //TODO: Make transferOwnership atomic.
@Override
- public void transferOwner(ComponentName admin, ComponentName target, PersistableBundle bundle) {
+ public void transferOwnership(ComponentName admin, ComponentName target, PersistableBundle bundle) {
if (!mHasFeature) {
return;
}
@@ -11874,16 +12134,20 @@
final DeviceAdminInfo incomingDeviceInfo = findAdmin(target, callingUserId,
/* throwForMissingPermission= */ true);
checkActiveAdminPrecondition(target, incomingDeviceInfo, policy);
+ if (!incomingDeviceInfo.getActivityInfo().metaData
+ .getBoolean(DeviceAdminReceiver.SUPPORT_TRANSFER_OWNERSHIP_META_DATA, false)) {
+ throw new IllegalArgumentException("Provided target does not support "
+ + "ownership transfer.");
+ }
final long id = mInjector.binderClearCallingIdentity();
try {
- //STOPSHIP add support for COMP, edge cases when device is rebooted/work mode off,
- //transfer callbacks and broadcast
+ //STOPSHIP add support for COMP, edge cases when device is rebooted/work mode off
synchronized (this) {
if (isProfileOwner(admin, callingUserId)) {
- transferProfileOwnerLocked(admin, target, callingUserId);
+ transferProfileOwnerLocked(admin, target, callingUserId, bundle);
} else if (isDeviceOwner(admin, callingUserId)) {
- transferDeviceOwnerLocked(admin, target, callingUserId);
+ transferDeviceOwnerLocked(admin, target, callingUserId, bundle);
}
}
} finally {
@@ -11895,24 +12159,119 @@
* Transfers the profile owner for user with id profileOwnerUserId from admin to target.
*/
private void transferProfileOwnerLocked(ComponentName admin, ComponentName target,
- int profileOwnerUserId) {
+ int profileOwnerUserId, PersistableBundle bundle) {
transferActiveAdminUncheckedLocked(target, admin, profileOwnerUserId);
mOwners.transferProfileOwner(target, profileOwnerUserId);
Slog.i(LOG_TAG, "Profile owner set: " + target + " on user " + profileOwnerUserId);
mOwners.writeProfileOwner(profileOwnerUserId);
mDeviceAdminServiceController.startServiceForOwner(
target.getPackageName(), profileOwnerUserId, "transfer-profile-owner");
+ sendProfileOwnerCommand(DeviceAdminReceiver.ACTION_TRANSFER_OWNERSHIP_COMPLETE,
+ getTransferOwnerAdminExtras(bundle), profileOwnerUserId);
+ sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED,
+ profileOwnerUserId);
}
/**
* Transfers the device owner for user with id userId from admin to target.
*/
- private void transferDeviceOwnerLocked(ComponentName admin, ComponentName target, int userId) {
+ private void transferDeviceOwnerLocked(ComponentName admin, ComponentName target, int userId,
+ PersistableBundle bundle) {
transferActiveAdminUncheckedLocked(target, admin, userId);
mOwners.transferDeviceOwner(target);
Slog.i(LOG_TAG, "Device owner set: " + target + " on user " + userId);
mOwners.writeDeviceOwner();
mDeviceAdminServiceController.startServiceForOwner(
target.getPackageName(), userId, "transfer-device-owner");
+ sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_TRANSFER_OWNERSHIP_COMPLETE,
+ getTransferOwnerAdminExtras(bundle));
+ sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED, userId);
+ }
+
+ private Bundle getTransferOwnerAdminExtras(PersistableBundle bundle) {
+ Bundle extras = new Bundle();
+ if (bundle != null) {
+ extras.putParcelable(EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE, bundle);
+ }
+ return extras;
+ }
+
+ @Override
+ public void setStartUserSessionMessage(
+ ComponentName admin, CharSequence startUserSessionMessage) {
+ if (!mHasFeature) {
+ return;
+ }
+ Preconditions.checkNotNull(admin);
+
+ final String startUserSessionMessageString =
+ startUserSessionMessage != null ? startUserSessionMessage.toString() : null;
+
+ synchronized (this) {
+ final ActiveAdmin deviceOwner =
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+ if (TextUtils.equals(deviceOwner.startUserSessionMessage, startUserSessionMessage)) {
+ return;
+ }
+ deviceOwner.startUserSessionMessage = startUserSessionMessageString;
+ saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ }
+
+ mInjector.getActivityManagerInternal()
+ .setSwitchingFromSystemUserMessage(startUserSessionMessageString);
+ }
+
+ @Override
+ public void setEndUserSessionMessage(ComponentName admin, CharSequence endUserSessionMessage) {
+ if (!mHasFeature) {
+ return;
+ }
+ Preconditions.checkNotNull(admin);
+
+ final String endUserSessionMessageString =
+ endUserSessionMessage != null ? endUserSessionMessage.toString() : null;
+
+ synchronized (this) {
+ final ActiveAdmin deviceOwner =
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+ if (TextUtils.equals(deviceOwner.endUserSessionMessage, endUserSessionMessage)) {
+ return;
+ }
+ deviceOwner.endUserSessionMessage = endUserSessionMessageString;
+ saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ }
+
+ mInjector.getActivityManagerInternal()
+ .setSwitchingToSystemUserMessage(endUserSessionMessageString);
+ }
+
+ @Override
+ public String getStartUserSessionMessage(ComponentName admin) {
+ if (!mHasFeature) {
+ return null;
+ }
+ Preconditions.checkNotNull(admin);
+
+ synchronized (this) {
+ final ActiveAdmin deviceOwner =
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ return deviceOwner.startUserSessionMessage;
+ }
+ }
+
+ @Override
+ public String getEndUserSessionMessage(ComponentName admin) {
+ if (!mHasFeature) {
+ return null;
+ }
+ Preconditions.checkNotNull(admin);
+
+ synchronized (this) {
+ final ActiveAdmin deviceOwner =
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ return deviceOwner.endUserSessionMessage;
+ }
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4310a98..3199bfa 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -80,6 +80,7 @@
import com.android.server.lights.LightsService;
import com.android.server.media.MediaResourceMonitorService;
import com.android.server.media.MediaRouterService;
+import com.android.server.media.MediaUpdateService;
import com.android.server.media.MediaSessionService;
import com.android.server.media.projection.MediaProjectionManagerService;
import com.android.server.net.NetworkPolicyManagerService;
@@ -1442,6 +1443,10 @@
mSystemServiceManager.startService(MediaSessionService.class);
traceEnd();
+ traceBeginAndSlog("StartMediaUpdateService");
+ mSystemServiceManager.startService(MediaUpdateService.class);
+ traceEnd();
+
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) {
traceBeginAndSlog("StartHdmiControlService");
mSystemServiceManager.startService(HdmiControlService.class);
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 31a1abb..7d9736e 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -38,6 +38,7 @@
import android.net.metrics.ApfStats;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.RaEvent;
+import android.net.util.InterfaceParams;
import android.system.ErrnoException;
import android.system.Os;
import android.system.PacketSocketAddress;
@@ -56,7 +57,6 @@
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
-import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
@@ -247,7 +247,7 @@
private final ApfCapabilities mApfCapabilities;
private final IpClient.Callback mIpClientCallback;
- private final NetworkInterface mNetworkInterface;
+ private final InterfaceParams mInterfaceParams;
private final IpConnectivityLog mMetricsLog;
@VisibleForTesting
@@ -269,11 +269,11 @@
private int mIPv4PrefixLength;
@VisibleForTesting
- ApfFilter(ApfConfiguration config, NetworkInterface networkInterface,
+ ApfFilter(ApfConfiguration config, InterfaceParams ifParams,
IpClient.Callback ipClientCallback, IpConnectivityLog log) {
mApfCapabilities = config.apfCapabilities;
mIpClientCallback = ipClientCallback;
- mNetworkInterface = networkInterface;
+ mInterfaceParams = ifParams;
mMulticastFilter = config.multicastFilter;
mDrop802_3Frames = config.ieee802_3Filter;
@@ -287,7 +287,7 @@
}
private void log(String s) {
- Log.d(TAG, "(" + mNetworkInterface.getName() + "): " + s);
+ Log.d(TAG, "(" + mInterfaceParams.name + "): " + s);
}
@GuardedBy("this")
@@ -332,14 +332,14 @@
void maybeStartFilter() {
FileDescriptor socket;
try {
- mHardwareAddress = mNetworkInterface.getHardwareAddress();
+ mHardwareAddress = mInterfaceParams.macAddr.toByteArray();
synchronized(this) {
// Install basic filters
installNewProgramLocked();
}
socket = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IPV6);
- PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IPV6,
- mNetworkInterface.getIndex());
+ PacketSocketAddress addr = new PacketSocketAddress(
+ (short) ETH_P_IPV6, mInterfaceParams.index);
Os.bind(socket, addr);
NetworkUtils.attachRaFilter(socket, mApfCapabilities.apfPacketFormat);
} catch(SocketException|ErrnoException e) {
@@ -1168,10 +1168,10 @@
* filtering using APF programs.
*/
public static ApfFilter maybeCreate(ApfConfiguration config,
- NetworkInterface networkInterface, IpClient.Callback ipClientCallback) {
- if (config == null) return null;
+ InterfaceParams ifParams, IpClient.Callback ipClientCallback) {
+ if (config == null || ifParams == null) return null;
ApfCapabilities apfCapabilities = config.apfCapabilities;
- if (apfCapabilities == null || networkInterface == null) return null;
+ if (apfCapabilities == null) return null;
if (apfCapabilities.apfVersionSupported == 0) return null;
if (apfCapabilities.maximumApfProgramSize < 512) {
Log.e(TAG, "Unacceptably small APF limit: " + apfCapabilities.maximumApfProgramSize);
@@ -1186,7 +1186,7 @@
Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported);
return null;
}
- return new ApfFilter(config, networkInterface, ipClientCallback, new IpConnectivityLog());
+ return new ApfFilter(config, ifParams, ipClientCallback, new IpConnectivityLog());
}
public synchronized void shutdown() {
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index ed78175..a956cef 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -34,6 +34,7 @@
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.DhcpClientEvent;
import android.net.metrics.DhcpErrorEvent;
+import android.net.util.InterfaceParams;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -50,7 +51,6 @@
import java.io.IOException;
import java.lang.Thread;
import java.net.Inet4Address;
-import java.net.NetworkInterface;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.util.Arrays;
@@ -187,7 +187,8 @@
private final String mIfaceName;
private boolean mRegisteredForPreDhcpNotification;
- private NetworkInterface mIface;
+ private InterfaceParams mIface;
+ // TODO: MacAddress-ify more of this class hierarchy.
private byte[] mHwAddr;
private PacketSocketAddress mInterfaceBroadcastAddr;
private int mTransactionId;
@@ -221,8 +222,9 @@
return new WakeupMessage(mContext, getHandler(), cmdName, cmd);
}
+ // TODO: Take an InterfaceParams instance instead of an interface name String.
private DhcpClient(Context context, StateMachine controller, String iface) {
- super(TAG);
+ super(TAG, controller.getHandler());
mContext = context;
mController = controller;
@@ -262,23 +264,23 @@
}
public static DhcpClient makeDhcpClient(
- Context context, StateMachine controller, String intf) {
- DhcpClient client = new DhcpClient(context, controller, intf);
+ Context context, StateMachine controller, InterfaceParams ifParams) {
+ DhcpClient client = new DhcpClient(context, controller, ifParams.name);
+ client.mIface = ifParams;
client.start();
return client;
}
private boolean initInterface() {
- try {
- mIface = NetworkInterface.getByName(mIfaceName);
- mHwAddr = mIface.getHardwareAddress();
- mInterfaceBroadcastAddr = new PacketSocketAddress(mIface.getIndex(),
- DhcpPacket.ETHER_BROADCAST);
- return true;
- } catch(SocketException | NullPointerException e) {
- Log.e(TAG, "Can't determine ifindex or MAC address for " + mIfaceName, e);
+ if (mIface == null) mIface = InterfaceParams.getByName(mIfaceName);
+ if (mIface == null) {
+ Log.e(TAG, "Can't determine InterfaceParams for " + mIfaceName);
return false;
}
+
+ mHwAddr = mIface.macAddr.toByteArray();
+ mInterfaceBroadcastAddr = new PacketSocketAddress(mIface.index, DhcpPacket.ETHER_BROADCAST);
+ return true;
}
private void startNewTransaction() {
@@ -293,7 +295,7 @@
private boolean initPacketSocket() {
try {
mPacketSock = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IP);
- PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IP, mIface.getIndex());
+ PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IP, mIface.index);
Os.bind(mPacketSock, addr);
NetworkUtils.attachDhcpFilter(mPacketSock);
} catch(SocketException|ErrnoException e) {
diff --git a/services/net/java/android/net/ip/ConnectivityPacketTracker.java b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
index 6cf4fa9a..e6ddbbc 100644
--- a/services/net/java/android/net/ip/ConnectivityPacketTracker.java
+++ b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
@@ -21,6 +21,7 @@
import android.net.NetworkUtils;
import android.net.util.PacketReader;
import android.net.util.ConnectivityPacketSummary;
+import android.net.util.InterfaceParams;
import android.os.Handler;
import android.system.ErrnoException;
import android.system.Os;
@@ -35,7 +36,6 @@
import java.io.FileDescriptor;
import java.io.InterruptedIOException;
import java.io.IOException;
-import java.net.NetworkInterface;
import java.net.SocketException;
@@ -69,24 +69,12 @@
private boolean mRunning;
private String mDisplayName;
- public ConnectivityPacketTracker(Handler h, NetworkInterface netif, LocalLog log) {
- final String ifname;
- final int ifindex;
- final byte[] hwaddr;
- final int mtu;
+ public ConnectivityPacketTracker(Handler h, InterfaceParams ifParams, LocalLog log) {
+ if (ifParams == null) throw new IllegalArgumentException("null InterfaceParams");
- try {
- ifname = netif.getName();
- ifindex = netif.getIndex();
- hwaddr = netif.getHardwareAddress();
- mtu = netif.getMTU();
- } catch (NullPointerException|SocketException e) {
- throw new IllegalArgumentException("bad network interface", e);
- }
-
- mTag = TAG + "." + ifname;
+ mTag = TAG + "." + ifParams.name;
mLog = log;
- mPacketListener = new PacketListener(h, ifindex, hwaddr, mtu);
+ mPacketListener = new PacketListener(h, ifParams);
}
public void start(String displayName) {
@@ -102,13 +90,11 @@
}
private final class PacketListener extends PacketReader {
- private final int mIfIndex;
- private final byte mHwAddr[];
+ private final InterfaceParams mInterface;
- PacketListener(Handler h, int ifindex, byte[] hwaddr, int mtu) {
- super(h, mtu);
- mIfIndex = ifindex;
- mHwAddr = hwaddr;
+ PacketListener(Handler h, InterfaceParams ifParams) {
+ super(h, ifParams.defaultMtu);
+ mInterface = ifParams;
}
@Override
@@ -117,7 +103,7 @@
try {
s = Os.socket(AF_PACKET, SOCK_RAW, 0);
NetworkUtils.attachControlPacketFilter(s, ARPHRD_ETHER);
- Os.bind(s, new PacketSocketAddress((short) ETH_P_ALL, mIfIndex));
+ Os.bind(s, new PacketSocketAddress((short) ETH_P_ALL, mInterface.index));
} catch (ErrnoException | IOException e) {
logError("Failed to create packet tracking socket: ", e);
closeFd(s);
@@ -129,7 +115,7 @@
@Override
protected void handlePacket(byte[] recvbuf, int length) {
final String summary = ConnectivityPacketSummary.summarize(
- mHwAddr, recvbuf, length);
+ mInterface.macAddr, recvbuf, length);
if (summary == null) return;
if (DBG) Log.d(mTag, summary);
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index fdb366c..d3a97b3 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -35,6 +35,7 @@
import android.net.dhcp.DhcpClient;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.IpManagerEvent;
+import android.net.util.InterfaceParams;
import android.net.util.MultinetworkPolicyTracker;
import android.net.util.NetdService;
import android.net.util.NetworkConstants;
@@ -63,7 +64,6 @@
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
-import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Collection;
@@ -556,7 +556,7 @@
private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
private final InterfaceController mInterfaceCtrl;
- private NetworkInterface mNetworkInterface;
+ private InterfaceParams mInterfaceParams;
/**
* Non-final member variables accessed only from within our StateMachine.
@@ -722,7 +722,12 @@
return;
}
- getNetworkInterface();
+ mInterfaceParams = InterfaceParams.getByName(mInterfaceName);
+ if (mInterfaceParams == null) {
+ logError("Failed to find InterfaceParams for " + mInterfaceName);
+ // TODO: call doImmediateProvisioningFailure() with an error code
+ // indicating something like "interface not ready".
+ }
mCallback.setNeighborDiscoveryOffload(true);
sendMessage(CMD_START, new ProvisioningConfiguration(req));
@@ -858,7 +863,7 @@
protected String getLogRecString(Message msg) {
final String logLine = String.format(
"%s/%d %d %d %s [%s]",
- mInterfaceName, mNetworkInterface == null ? -1 : mNetworkInterface.getIndex(),
+ mInterfaceName, (mInterfaceParams == null) ? -1 : mInterfaceParams.index,
msg.arg1, msg.arg2, Objects.toString(msg.obj), mMsgStateLogger);
final String richerLogLine = getWhatToString(msg.what) + " " + logLine;
@@ -889,15 +894,6 @@
mLog.log(msg);
}
- private void getNetworkInterface() {
- try {
- mNetworkInterface = NetworkInterface.getByName(mInterfaceName);
- } catch (SocketException | NullPointerException e) {
- // TODO: throw new IllegalStateException.
- logError("Failed to get interface object: %s", e);
- }
- }
-
// This needs to be called with care to ensure that our LinkProperties
// are in sync with the actual LinkProperties of the interface. For example,
// we should only call this if we know for sure that there are no IP addresses
@@ -1218,7 +1214,7 @@
}
} else {
// Start DHCPv4.
- mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceName);
+ mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams);
mDhcpClient.registerForPreDhcpNotification();
mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
}
@@ -1245,7 +1241,7 @@
try {
mIpReachabilityMonitor = new IpReachabilityMonitor(
mContext,
- mInterfaceName,
+ mInterfaceParams,
getHandler(),
mLog,
new IpReachabilityMonitor.Callback() {
@@ -1447,7 +1443,7 @@
mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames);
apfConfig.ethTypeBlackList =
mContext.getResources().getIntArray(R.array.config_apfEthTypeBlackList);
- mApfFilter = ApfFilter.maybeCreate(apfConfig, mNetworkInterface, mCallback);
+ mApfFilter = ApfFilter.maybeCreate(apfConfig, mInterfaceParams, mCallback);
// TODO: investigate the effects of any multicast filtering racing/interfering with the
// rest of this IP configuration startup.
if (mApfFilter == null) {
@@ -1515,7 +1511,7 @@
private ConnectivityPacketTracker createPacketTracker() {
try {
return new ConnectivityPacketTracker(
- getHandler(), mNetworkInterface, mConnectivityPacketLog);
+ getHandler(), mInterfaceParams, mConnectivityPacketLog);
} catch (IllegalArgumentException e) {
return null;
}
diff --git a/services/net/java/android/net/ip/IpNeighborMonitor.java b/services/net/java/android/net/ip/IpNeighborMonitor.java
index 6807334..fc07aa1 100644
--- a/services/net/java/android/net/ip/IpNeighborMonitor.java
+++ b/services/net/java/android/net/ip/IpNeighborMonitor.java
@@ -16,7 +16,11 @@
package android.net.ip;
-import android.net.netlink.NetlinkConstants;
+import static android.net.netlink.NetlinkConstants.hexify;
+import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH;
+import static android.net.netlink.NetlinkConstants.stringForNlMsgType;
+
+import android.net.MacAddress;
import android.net.netlink.NetlinkErrorMessage;
import android.net.netlink.NetlinkMessage;
import android.net.netlink.NetlinkSocket;
@@ -92,37 +96,35 @@
final int ifindex;
final InetAddress ip;
final short nudState;
- final byte[] linkLayerAddr;
+ final MacAddress macAddr;
public NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip,
- short nudState, byte[] linkLayerAddr) {
+ short nudState, MacAddress macAddr) {
this.elapsedMs = elapsedMs;
this.msgType = msgType;
this.ifindex = ifindex;
this.ip = ip;
this.nudState = nudState;
- this.linkLayerAddr = linkLayerAddr;
+ this.macAddr = macAddr;
}
boolean isConnected() {
- return (msgType != NetlinkConstants.RTM_DELNEIGH) &&
- StructNdMsg.isNudStateConnected(nudState);
+ return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateConnected(nudState);
}
boolean isValid() {
- return (msgType != NetlinkConstants.RTM_DELNEIGH) &&
- StructNdMsg.isNudStateValid(nudState);
+ return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateValid(nudState);
}
@Override
public String toString() {
final StringJoiner j = new StringJoiner(",", "NeighborEvent{", "}");
return j.add("@" + elapsedMs)
- .add(NetlinkConstants.stringForNlMsgType(msgType))
+ .add(stringForNlMsgType(msgType))
.add("if=" + ifindex)
.add(ip.getHostAddress())
.add(StructNdMsg.stringForNudState(nudState))
- .add("[" + NetlinkConstants.hexify(linkLayerAddr) + "]")
+ .add("[" + macAddr + "]")
.toString();
}
}
@@ -183,7 +185,7 @@
final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer);
if (nlMsg == null || nlMsg.getHeader() == null) {
byteBuffer.position(position);
- mLog.e("unparsable netlink msg: " + NetlinkConstants.hexify(byteBuffer));
+ mLog.e("unparsable netlink msg: " + hexify(byteBuffer));
break;
}
@@ -217,12 +219,13 @@
final int ifindex = ndMsg.ndm_ifindex;
final InetAddress destination = neighMsg.getDestination();
final short nudState =
- (msgType == NetlinkConstants.RTM_DELNEIGH)
+ (msgType == RTM_DELNEIGH)
? StructNdMsg.NUD_NONE
: ndMsg.ndm_state;
final NeighborEvent event = new NeighborEvent(
- whenMs, msgType, ifindex, destination, nudState, neighMsg.getLinkLayerAddress());
+ whenMs, msgType, ifindex, destination, nudState,
+ getMacAddress(neighMsg.getLinkLayerAddress()));
if (VDBG) {
Log.d(TAG, neighMsg.toString());
@@ -233,4 +236,16 @@
mConsumer.accept(event);
}
+
+ private static MacAddress getMacAddress(byte[] linkLayerAddress) {
+ if (linkLayerAddress != null) {
+ try {
+ return MacAddress.fromBytes(linkLayerAddress);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Failed to parse link-layer address: " + hexify(linkLayerAddress));
+ }
+ }
+
+ return null;
+ }
}
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index b31ffbb..7e02a28 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -26,6 +26,7 @@
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.IpReachabilityEvent;
import android.net.netlink.StructNdMsg;
+import android.net.util.InterfaceParams;
import android.net.util.MultinetworkPolicyTracker;
import android.net.util.SharedLog;
import android.os.Handler;
@@ -46,9 +47,7 @@
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
-import java.net.NetworkInterface;
import java.net.SocketAddress;
-import java.net.SocketException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
@@ -168,8 +167,7 @@
}
}
- private final String mInterfaceName;
- private final int mInterfaceIndex;
+ private final InterfaceParams mInterfaceParams;
private final IpNeighborMonitor mIpNeighborMonitor;
private final SharedLog mLog;
private final Callback mCallback;
@@ -182,30 +180,25 @@
private volatile long mLastProbeTimeMs;
public IpReachabilityMonitor(
- Context context, String ifName, Handler h, SharedLog log, Callback callback) {
- this(context, ifName, h, log, callback, null);
- }
-
- public IpReachabilityMonitor(
- Context context, String ifName, Handler h, SharedLog log, Callback callback,
+ Context context, InterfaceParams ifParams, Handler h, SharedLog log, Callback callback,
MultinetworkPolicyTracker tracker) {
- this(ifName, getInterfaceIndex(ifName), h, log, callback, tracker,
- Dependencies.makeDefault(context, ifName));
+ this(ifParams, h, log, callback, tracker, Dependencies.makeDefault(context, ifParams.name));
}
@VisibleForTesting
- IpReachabilityMonitor(String ifName, int ifIndex, Handler h, SharedLog log, Callback callback,
+ IpReachabilityMonitor(InterfaceParams ifParams, Handler h, SharedLog log, Callback callback,
MultinetworkPolicyTracker tracker, Dependencies dependencies) {
- mInterfaceName = ifName;
+ if (ifParams == null) throw new IllegalArgumentException("null InterfaceParams");
+
+ mInterfaceParams = ifParams;
mLog = log.forSubComponent(TAG);
mCallback = callback;
mMultinetworkPolicyTracker = tracker;
- mInterfaceIndex = ifIndex;
mDependencies = dependencies;
mIpNeighborMonitor = new IpNeighborMonitor(h, mLog,
(NeighborEvent event) -> {
- if (mInterfaceIndex != event.ifindex) return;
+ if (mInterfaceParams.index != event.ifindex) return;
if (!mNeighborWatchList.containsKey(event.ip)) return;
final NeighborEvent prev = mNeighborWatchList.put(event.ip, event);
@@ -241,7 +234,7 @@
private String describeWatchList(String sep) {
final StringBuilder sb = new StringBuilder();
- sb.append("iface{" + mInterfaceName + "/" + mInterfaceIndex + "}," + sep);
+ sb.append("iface{" + mInterfaceParams + "}," + sep);
sb.append("ntable=[" + sep);
String delimiter = "";
for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) {
@@ -262,10 +255,10 @@
}
public void updateLinkProperties(LinkProperties lp) {
- if (!mInterfaceName.equals(lp.getInterfaceName())) {
+ if (!mInterfaceParams.name.equals(lp.getInterfaceName())) {
// TODO: figure out whether / how to cope with interface changes.
Log.wtf(TAG, "requested LinkProperties interface '" + lp.getInterfaceName() +
- "' does not match: " + mInterfaceName);
+ "' does not match: " + mInterfaceParams.name);
return;
}
@@ -353,10 +346,10 @@
mDependencies.acquireWakeLock(getProbeWakeLockDuration());
}
- for (InetAddress target : ipProbeList) {
- final int rval = IpNeighborMonitor.startKernelNeighborProbe(mInterfaceIndex, target);
+ for (InetAddress ip : ipProbeList) {
+ final int rval = IpNeighborMonitor.startKernelNeighborProbe(mInterfaceParams.index, ip);
mLog.log(String.format("put neighbor %s into NUD_PROBE state (rval=%d)",
- target.getHostAddress(), rval));
+ ip.getHostAddress(), rval));
logEvent(IpReachabilityEvent.PROBE, rval);
}
mLastProbeTimeMs = SystemClock.elapsedRealtime();
@@ -378,22 +371,9 @@
return (numUnicastProbes * retransTimeMs) + gracePeriodMs;
}
- private static int getInterfaceIndex(String ifname) {
- final NetworkInterface iface;
- try {
- iface = NetworkInterface.getByName(ifname);
- } catch (SocketException e) {
- throw new IllegalArgumentException("invalid interface '" + ifname + "': ", e);
- }
- if (iface == null) {
- throw new IllegalArgumentException("NetworkInterface was null for " + ifname);
- }
- return iface.getIndex();
- }
-
private void logEvent(int probeType, int errorCode) {
int eventType = probeType | (errorCode & 0xff);
- mMetricsLog.log(mInterfaceName, new IpReachabilityEvent(eventType));
+ mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType));
}
private void logNudFailed(ProvisioningChange delta) {
@@ -401,6 +381,6 @@
boolean isFromProbe = (duration < getProbeWakeLockDuration());
boolean isProvisioningLost = (delta == ProvisioningChange.LOST_PROVISIONING);
int eventType = IpReachabilityEvent.nudFailureEventType(isFromProbe, isProvisioningLost);
- mMetricsLog.log(mInterfaceName, new IpReachabilityEvent(eventType));
+ mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType));
}
}
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
index cb3123c..49a1e79 100644
--- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
+++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
@@ -25,6 +25,7 @@
import android.net.LinkProperties;
import android.net.NetworkUtils;
import android.net.TrafficStats;
+import android.net.util.InterfaceParams;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructGroupReq;
@@ -96,9 +97,7 @@
(byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
};
- private final String mIfName;
- private final int mIfIndex;
- private final byte[] mHwAddr;
+ private final InterfaceParams mInterface;
private final InetSocketAddress mAllNodes;
// This lock is to protect the RA from being updated while being
@@ -223,11 +222,9 @@
}
- public RouterAdvertisementDaemon(String ifname, int ifindex, byte[] hwaddr) {
- mIfName = ifname;
- mIfIndex = ifindex;
- mHwAddr = hwaddr;
- mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mIfIndex), 0);
+ public RouterAdvertisementDaemon(InterfaceParams ifParams) {
+ mInterface = ifParams;
+ mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mInterface.index), 0);
mDeprecatedInfoTracker = new DeprecatedInfoTracker();
}
@@ -279,7 +276,7 @@
try {
putHeader(ra, mRaParams != null && mRaParams.hasDefaultRoute);
- putSlla(ra, mHwAddr);
+ putSlla(ra, mInterface.macAddr.toByteArray());
mRaLength = ra.position();
// https://tools.ietf.org/html/rfc5175#section-4 says:
@@ -579,9 +576,9 @@
// Setting SNDTIMEO is purely for defensive purposes.
Os.setsockoptTimeval(
mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(SEND_TIMEOUT_MS));
- Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mIfName);
+ Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mInterface.name);
NetworkUtils.protectFromVpn(mSocket);
- NetworkUtils.setupRaSocket(mSocket, mIfIndex);
+ NetworkUtils.setupRaSocket(mSocket, mInterface.index);
} catch (ErrnoException | IOException e) {
Log.e(TAG, "Failed to create RA daemon socket: " + e);
return false;
@@ -614,7 +611,7 @@
final InetAddress destip = dest.getAddress();
return (destip instanceof Inet6Address) &&
destip.isLinkLocalAddress() &&
- (((Inet6Address) destip).getScopeId() == mIfIndex);
+ (((Inet6Address) destip).getScopeId() == mInterface.index);
}
private void maybeSendRA(InetSocketAddress dest) {
diff --git a/services/net/java/android/net/util/ConnectivityPacketSummary.java b/services/net/java/android/net/util/ConnectivityPacketSummary.java
index dae93af..4951400 100644
--- a/services/net/java/android/net/util/ConnectivityPacketSummary.java
+++ b/services/net/java/android/net/util/ConnectivityPacketSummary.java
@@ -17,6 +17,7 @@
package android.net.util;
import android.net.dhcp.DhcpPacket;
+import android.net.MacAddress;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -45,21 +46,20 @@
private final ByteBuffer mPacket;
private final String mSummary;
- public static String summarize(byte[] hwaddr, byte[] buffer) {
+ public static String summarize(MacAddress hwaddr, byte[] buffer) {
return summarize(hwaddr, buffer, buffer.length);
}
// Methods called herein perform some but by no means all error checking.
// They may throw runtime exceptions on malformed packets.
- public static String summarize(byte[] hwaddr, byte[] buffer, int length) {
- if ((hwaddr == null) || (hwaddr.length != ETHER_ADDR_LEN)) return null;
- if (buffer == null) return null;
+ public static String summarize(MacAddress macAddr, byte[] buffer, int length) {
+ if ((macAddr == null) || (buffer == null)) return null;
length = Math.min(length, buffer.length);
- return (new ConnectivityPacketSummary(hwaddr, buffer, length)).toString();
+ return (new ConnectivityPacketSummary(macAddr, buffer, length)).toString();
}
- private ConnectivityPacketSummary(byte[] hwaddr, byte[] buffer, int length) {
- mHwAddr = hwaddr;
+ private ConnectivityPacketSummary(MacAddress macAddr, byte[] buffer, int length) {
+ mHwAddr = macAddr.toByteArray();
mBytes = buffer;
mLength = Math.min(length, mBytes.length);
mPacket = ByteBuffer.wrap(mBytes, 0, mLength);
diff --git a/services/net/java/android/net/util/InterfaceParams.java b/services/net/java/android/net/util/InterfaceParams.java
new file mode 100644
index 0000000..a4b2fbb
--- /dev/null
+++ b/services/net/java/android/net/util/InterfaceParams.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 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.net.util;
+
+import static android.net.MacAddress.ALL_ZEROS_ADDRESS;
+import static android.net.util.NetworkConstants.ETHER_MTU;
+import static android.net.util.NetworkConstants.IPV6_MIN_MTU;
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import android.net.MacAddress;
+import android.text.TextUtils;
+
+import java.net.NetworkInterface;
+import java.net.SocketException;
+
+
+/**
+ * Encapsulate the interface parameters common to IpClient/IpServer components.
+ *
+ * Basically all java.net.NetworkInterface methods throw Exceptions. IpClient
+ * and IpServer (sub)components need most or all of this information at some
+ * point during their lifecycles, so pass only this simplified object around
+ * which can be created once when IpClient/IpServer are told to start.
+ *
+ * @hide
+ */
+public class InterfaceParams {
+ public final String name;
+ public final int index;
+ public final MacAddress macAddr;
+ public final int defaultMtu;
+
+ public static InterfaceParams getByName(String name) {
+ final NetworkInterface netif = getNetworkInterfaceByName(name);
+ if (netif == null) return null;
+
+ // Not all interfaces have MAC addresses, e.g. rmnet_data0.
+ final MacAddress macAddr = getMacAddress(netif);
+
+ try {
+ return new InterfaceParams(name, netif.getIndex(), macAddr, netif.getMTU());
+ } catch (IllegalArgumentException|SocketException e) {
+ return null;
+ }
+ }
+
+ public InterfaceParams(String name, int index, MacAddress macAddr) {
+ this(name, index, macAddr, ETHER_MTU);
+ }
+
+ public InterfaceParams(String name, int index, MacAddress macAddr, int defaultMtu) {
+ checkArgument((!TextUtils.isEmpty(name)), "impossible interface name");
+ checkArgument((index > 0), "invalid interface index");
+ this.name = name;
+ this.index = index;
+ this.macAddr = (macAddr != null) ? macAddr : ALL_ZEROS_ADDRESS;
+ this.defaultMtu = (defaultMtu > IPV6_MIN_MTU) ? defaultMtu : IPV6_MIN_MTU;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s/%d/%s/%d", name, index, macAddr, defaultMtu);
+ }
+
+ private static NetworkInterface getNetworkInterfaceByName(String name) {
+ try {
+ return NetworkInterface.getByName(name);
+ } catch (NullPointerException|SocketException e) {
+ return null;
+ }
+ }
+
+ private static MacAddress getMacAddress(NetworkInterface netif) {
+ try {
+ return MacAddress.fromBytes(netif.getHardwareAddress());
+ } catch (IllegalArgumentException|NullPointerException|SocketException e) {
+ return null;
+ }
+ }
+}
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 71ba685..cd4e8f9 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -57,7 +57,9 @@
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.print.DualDumpOutputStream;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.SystemService;
@@ -670,37 +672,29 @@
final long identity = Binder.clearCallingIdentity();
try {
if (dumpAsProto) {
- dump(new ProtoOutputStream(fd), userStatesToDump);
+ dump(new DualDumpOutputStream(new ProtoOutputStream(fd), null),
+ userStatesToDump);
} else {
- dump(fd, pw, userStatesToDump);
+ pw.println("PRINT MANAGER STATE (dumpsys print)");
+
+ dump(new DualDumpOutputStream(null, new IndentingPrintWriter(pw, " ")),
+ userStatesToDump);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- private void dump(@NonNull ProtoOutputStream proto,
+ private void dump(@NonNull DualDumpOutputStream dumpStream,
@NonNull ArrayList<UserState> userStatesToDump) {
final int userStateCount = userStatesToDump.size();
for (int i = 0; i < userStateCount; i++) {
- long token = proto.start(PrintServiceDumpProto.USER_STATES);
- userStatesToDump.get(i).dump(proto);
- proto.end(token);
+ long token = dumpStream.start("user_states", PrintServiceDumpProto.USER_STATES);
+ userStatesToDump.get(i).dump(dumpStream);
+ dumpStream.end(token);
}
- proto.flush();
- }
-
- private void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
- @NonNull ArrayList<UserState> userStatesToDump) {
- pw = Preconditions.checkNotNull(pw);
-
- pw.println("PRINT MANAGER STATE (dumpsys print)");
- final int userStateCount = userStatesToDump.size();
- for (int i = 0; i < userStateCount; i++) {
- userStatesToDump.get(i).dump(fd, pw, "");
- pw.println();
- }
+ dumpStream.flush();
}
private void registerContentObservers() {
diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java
index 13462cd..80b97cf 100644
--- a/services/print/java/com/android/server/print/RemotePrintService.java
+++ b/services/print/java/com/android/server/print/RemotePrintService.java
@@ -47,11 +47,10 @@
import android.printservice.IPrintServiceClient;
import android.service.print.ActivePrintServiceProto;
import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.print.DualDumpOutputStream;
-import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -532,49 +531,30 @@
}
}
- public void dump(@NonNull ProtoOutputStream proto) {
- writeComponentName(proto, ActivePrintServiceProto.COMPONENT_NAME, mComponentName);
+ public void dump(@NonNull DualDumpOutputStream proto) {
+ writeComponentName(proto, "component_name", ActivePrintServiceProto.COMPONENT_NAME,
+ mComponentName);
- proto.write(ActivePrintServiceProto.IS_DESTROYED, mDestroyed);
- proto.write(ActivePrintServiceProto.IS_BOUND, isBound());
- proto.write(ActivePrintServiceProto.HAS_DISCOVERY_SESSION, mHasPrinterDiscoverySession);
- proto.write(ActivePrintServiceProto.HAS_ACTIVE_PRINT_JOBS, mHasActivePrintJobs);
- proto.write(ActivePrintServiceProto.IS_DISCOVERING_PRINTERS,
+ proto.write("is_destroyed", ActivePrintServiceProto.IS_DESTROYED, mDestroyed);
+ proto.write("is_bound", ActivePrintServiceProto.IS_BOUND, isBound());
+ proto.write("has_discovery_session", ActivePrintServiceProto.HAS_DISCOVERY_SESSION,
+ mHasPrinterDiscoverySession);
+ proto.write("has_active_print_jobs", ActivePrintServiceProto.HAS_ACTIVE_PRINT_JOBS,
+ mHasActivePrintJobs);
+ proto.write("is_discovering_printers", ActivePrintServiceProto.IS_DISCOVERING_PRINTERS,
mDiscoveryPriorityList != null);
synchronized (mLock) {
if (mTrackedPrinterList != null) {
int numTrackedPrinters = mTrackedPrinterList.size();
for (int i = 0; i < numTrackedPrinters; i++) {
- writePrinterId(proto, ActivePrintServiceProto.TRACKED_PRINTERS,
- mTrackedPrinterList.get(i));
+ writePrinterId(proto, "tracked_printers",
+ ActivePrintServiceProto.TRACKED_PRINTERS, mTrackedPrinterList.get(i));
}
}
}
}
- public void dump(PrintWriter pw, String prefix) {
- String tab = " ";
- pw.append(prefix).append("service:").println();
- pw.append(prefix).append(tab).append("componentName=")
- .append(mComponentName.flattenToString()).println();
- pw.append(prefix).append(tab).append("destroyed=")
- .append(String.valueOf(mDestroyed)).println();
- pw.append(prefix).append(tab).append("bound=")
- .append(String.valueOf(isBound())).println();
- pw.append(prefix).append(tab).append("hasDicoverySession=")
- .append(String.valueOf(mHasPrinterDiscoverySession)).println();
- pw.append(prefix).append(tab).append("hasActivePrintJobs=")
- .append(String.valueOf(mHasActivePrintJobs)).println();
- pw.append(prefix).append(tab).append("isDiscoveringPrinters=")
- .append(String.valueOf(mDiscoveryPriorityList != null)).println();
-
- synchronized (mLock) {
- pw.append(prefix).append(tab).append("trackedPrinters=").append(
- (mTrackedPrinterList != null) ? mTrackedPrinterList.toString() : "null");
- }
- }
-
private boolean isBound() {
return mPrintService != null;
}
diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java
index f654fcb..a69baa1 100644
--- a/services/print/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java
@@ -43,16 +43,14 @@
import android.service.print.PrintSpoolerStateProto;
import android.util.Slog;
import android.util.TimedRemoteCaller;
-import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.TransferPipe;
+import com.android.internal.print.DualDumpOutputStream;
import libcore.io.IoUtils;
-import java.io.FileDescriptor;
import java.io.IOException;
-import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.concurrent.TimeoutException;
@@ -558,37 +556,25 @@
}
}
- public void dump(@NonNull ProtoOutputStream proto) {
+ public void dump(@NonNull DualDumpOutputStream dumpStream) {
synchronized (mLock) {
- proto.write(PrintSpoolerStateProto.IS_DESTROYED, mDestroyed);
- proto.write(PrintSpoolerStateProto.IS_BOUND, mRemoteInstance != null);
+ dumpStream.write("is_destroyed", PrintSpoolerStateProto.IS_DESTROYED, mDestroyed);
+ dumpStream.write("is_bound", PrintSpoolerStateProto.IS_BOUND, mRemoteInstance != null);
}
try {
- proto.write(PrintSpoolerStateProto.INTERNAL_STATE,
- TransferPipe.dumpAsync(getRemoteInstanceLazy().asBinder(), "--proto"));
+ if (dumpStream.isProto()) {
+ dumpStream.write(null, PrintSpoolerStateProto.INTERNAL_STATE,
+ TransferPipe.dumpAsync(getRemoteInstanceLazy().asBinder(), "--proto"));
+ } else {
+ dumpStream.writeNested("internal_state", TransferPipe.dumpAsync(
+ getRemoteInstanceLazy().asBinder()));
+ }
} catch (IOException | TimeoutException | RemoteException | InterruptedException e) {
Slog.e(LOG_TAG, "Failed to dump remote instance", e);
}
}
- public void dump(FileDescriptor fd, PrintWriter pw, String prefix) {
- synchronized (mLock) {
- pw.append(prefix).append("destroyed=")
- .append(String.valueOf(mDestroyed)).println();
- pw.append(prefix).append("bound=")
- .append((mRemoteInstance != null) ? "true" : "false").println();
-
- pw.flush();
- try {
- TransferPipe.dumpAsync(getRemoteInstanceLazy().asBinder(), fd,
- new String[] { prefix });
- } catch (IOException | TimeoutException | RemoteException | InterruptedException e) {
- pw.println("Failed to dump remote instance: " + e);
- }
- }
- }
-
private void onAllPrintJobsHandled() {
synchronized (mLock) {
throwIfDestroyedLocked();
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 364bbc0..e2808e8 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -76,19 +76,17 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.print.DualDumpOutputStream;
import com.android.server.print.RemotePrintService.PrintServiceCallbacks;
import com.android.server.print.RemotePrintServiceRecommendationService
.RemotePrintServiceRecommendationServiceCallbacks;
import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@@ -817,112 +815,63 @@
mDestroyed = true;
}
- public void dump(@NonNull ProtoOutputStream proto) {
+ public void dump(@NonNull DualDumpOutputStream dumpStream) {
synchronized (mLock) {
- proto.write(PrintUserStateProto.USER_ID, mUserId);
+ dumpStream.write("user_id", PrintUserStateProto.USER_ID, mUserId);
final int installedServiceCount = mInstalledServices.size();
for (int i = 0; i < installedServiceCount; i++) {
- long token = proto.start(PrintUserStateProto.INSTALLED_SERVICES);
+ long token = dumpStream.start("installed_services",
+ PrintUserStateProto.INSTALLED_SERVICES);
PrintServiceInfo installedService = mInstalledServices.get(i);
ResolveInfo resolveInfo = installedService.getResolveInfo();
- writeComponentName(proto, InstalledPrintServiceProto.COMPONENT_NAME,
+ writeComponentName(dumpStream, "component_name",
+ InstalledPrintServiceProto.COMPONENT_NAME,
new ComponentName(resolveInfo.serviceInfo.packageName,
resolveInfo.serviceInfo.name));
- writeStringIfNotNull(proto, InstalledPrintServiceProto.SETTINGS_ACTIVITY,
+ writeStringIfNotNull(dumpStream, "settings_activity",
+ InstalledPrintServiceProto.SETTINGS_ACTIVITY,
installedService.getSettingsActivityName());
- writeStringIfNotNull(proto, InstalledPrintServiceProto.ADD_PRINTERS_ACTIVITY,
+ writeStringIfNotNull(dumpStream, "add_printers_activity",
+ InstalledPrintServiceProto.ADD_PRINTERS_ACTIVITY,
installedService.getAddPrintersActivityName());
- writeStringIfNotNull(proto, InstalledPrintServiceProto.ADVANCED_OPTIONS_ACTIVITY,
+ writeStringIfNotNull(dumpStream, "advanced_options_activity",
+ InstalledPrintServiceProto.ADVANCED_OPTIONS_ACTIVITY,
installedService.getAdvancedOptionsActivityName());
- proto.end(token);
+ dumpStream.end(token);
}
for (ComponentName disabledService : mDisabledServices) {
- writeComponentName(proto, PrintUserStateProto.DISABLED_SERVICES, disabledService);
+ writeComponentName(dumpStream, "disabled_services",
+ PrintUserStateProto.DISABLED_SERVICES, disabledService);
}
final int activeServiceCount = mActiveServices.size();
for (int i = 0; i < activeServiceCount; i++) {
- long token = proto.start(PrintUserStateProto.ACTIVE_SERVICES);
- mActiveServices.valueAt(i).dump(proto);
- proto.end(token);
+ long token = dumpStream.start("actives_services",
+ PrintUserStateProto.ACTIVE_SERVICES);
+ mActiveServices.valueAt(i).dump(dumpStream);
+ dumpStream.end(token);
}
- mPrintJobForAppCache.dumpLocked(proto);
+ mPrintJobForAppCache.dumpLocked(dumpStream);
if (mPrinterDiscoverySession != null) {
- long token = proto.start(PrintUserStateProto.DISCOVERY_SESSIONS);
- mPrinterDiscoverySession.dumpLocked(proto);
- proto.end(token);
+ long token = dumpStream.start("discovery_service",
+ PrintUserStateProto.DISCOVERY_SESSIONS);
+ mPrinterDiscoverySession.dumpLocked(dumpStream);
+ dumpStream.end(token);
}
}
- long token = proto.start(PrintUserStateProto.PRINT_SPOOLER_STATE);
- mSpooler.dump(proto);
- proto.end(token);
- }
-
- public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String prefix) {
- pw.append(prefix).append("user state ").append(String.valueOf(mUserId)).append(":");
- pw.println();
-
- String tab = " ";
-
- synchronized (mLock) {
- pw.append(prefix).append(tab).append("installed services:").println();
- final int installedServiceCount = mInstalledServices.size();
- for (int i = 0; i < installedServiceCount; i++) {
- PrintServiceInfo installedService = mInstalledServices.get(i);
- String installedServicePrefix = prefix + tab + tab;
- pw.append(installedServicePrefix).append("service:").println();
- ResolveInfo resolveInfo = installedService.getResolveInfo();
- ComponentName componentName = new ComponentName(
- resolveInfo.serviceInfo.packageName,
- resolveInfo.serviceInfo.name);
- pw.append(installedServicePrefix).append(tab).append("componentName=")
- .append(componentName.flattenToString()).println();
- pw.append(installedServicePrefix).append(tab).append("settingsActivity=")
- .append(installedService.getSettingsActivityName()).println();
- pw.append(installedServicePrefix).append(tab).append("addPrintersActivity=")
- .append(installedService.getAddPrintersActivityName()).println();
- pw.append(installedServicePrefix).append(tab).append("avancedOptionsActivity=")
- .append(installedService.getAdvancedOptionsActivityName()).println();
- }
-
- pw.append(prefix).append(tab).append("disabled services:").println();
- for (ComponentName disabledService : mDisabledServices) {
- String disabledServicePrefix = prefix + tab + tab;
- pw.append(disabledServicePrefix).append("service:").println();
- pw.append(disabledServicePrefix).append(tab).append("componentName=")
- .append(disabledService.flattenToString());
- pw.println();
- }
-
- pw.append(prefix).append(tab).append("active services:").println();
- final int activeServiceCount = mActiveServices.size();
- for (int i = 0; i < activeServiceCount; i++) {
- RemotePrintService activeService = mActiveServices.valueAt(i);
- activeService.dump(pw, prefix + tab + tab);
- pw.println();
- }
-
- pw.append(prefix).append(tab).append("cached print jobs:").println();
- mPrintJobForAppCache.dumpLocked(pw, prefix + tab + tab);
-
- pw.append(prefix).append(tab).append("discovery mediator:").println();
- if (mPrinterDiscoverySession != null) {
- mPrinterDiscoverySession.dumpLocked(pw, prefix + tab + tab);
- }
- }
-
- pw.append(prefix).append(tab).append("print spooler:").println();
- mSpooler.dump(fd, pw, prefix + tab + tab);
- pw.println();
+ long token = dumpStream.start("print_spooler_state",
+ PrintUserStateProto.PRINT_SPOOLER_STATE);
+ mSpooler.dump(dumpStream);
+ dumpStream.end(token);
}
private void readConfigurationLocked() {
@@ -1650,15 +1599,17 @@
}
}
- public void dumpLocked(@NonNull ProtoOutputStream proto) {
- proto.write(PrinterDiscoverySessionProto.IS_DESTROYED, mDestroyed);
- proto.write(PrinterDiscoverySessionProto.IS_PRINTER_DISCOVERY_IN_PROGRESS,
+ public void dumpLocked(@NonNull DualDumpOutputStream dumpStream) {
+ dumpStream.write("is_destroyed", PrinterDiscoverySessionProto.IS_DESTROYED, mDestroyed);
+ dumpStream.write("is_printer_discovery_in_progress",
+ PrinterDiscoverySessionProto.IS_PRINTER_DISCOVERY_IN_PROGRESS,
!mStartedPrinterDiscoveryTokens.isEmpty());
final int observerCount = mDiscoveryObservers.beginBroadcast();
for (int i = 0; i < observerCount; i++) {
IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i);
- proto.write(PrinterDiscoverySessionProto.PRINTER_DISCOVERY_OBSERVERS,
+ dumpStream.write("printer_discovery_observers",
+ PrinterDiscoverySessionProto.PRINTER_DISCOVERY_OBSERVERS,
observer.toString());
}
mDiscoveryObservers.finishBroadcast();
@@ -1666,61 +1617,22 @@
final int tokenCount = this.mStartedPrinterDiscoveryTokens.size();
for (int i = 0; i < tokenCount; i++) {
IBinder token = mStartedPrinterDiscoveryTokens.get(i);
- proto.write(PrinterDiscoverySessionProto.DISCOVERY_REQUESTS, token.toString());
+ dumpStream.write("discovery_requests",
+ PrinterDiscoverySessionProto.DISCOVERY_REQUESTS, token.toString());
}
final int trackedPrinters = mStateTrackedPrinters.size();
for (int i = 0; i < trackedPrinters; i++) {
PrinterId printer = mStateTrackedPrinters.get(i);
- writePrinterId(proto, PrinterDiscoverySessionProto.TRACKED_PRINTER_REQUESTS,
- printer);
+ writePrinterId(dumpStream, "tracked_printer_requests",
+ PrinterDiscoverySessionProto.TRACKED_PRINTER_REQUESTS, printer);
}
final int printerCount = mPrinters.size();
for (int i = 0; i < printerCount; i++) {
PrinterInfo printer = mPrinters.valueAt(i);
- writePrinterInfo(mContext, proto, PrinterDiscoverySessionProto.PRINTER, printer);
- }
- }
-
- public void dumpLocked(PrintWriter pw, String prefix) {
- pw.append(prefix).append("destroyed=")
- .append(String.valueOf(mDestroyed)).println();
-
- pw.append(prefix).append("printDiscoveryInProgress=")
- .append(String.valueOf(!mStartedPrinterDiscoveryTokens.isEmpty())).println();
-
- String tab = " ";
-
- pw.append(prefix).append(tab).append("printer discovery observers:").println();
- final int observerCount = mDiscoveryObservers.beginBroadcast();
- for (int i = 0; i < observerCount; i++) {
- IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i);
- pw.append(prefix).append(prefix).append(observer.toString());
- pw.println();
- }
- mDiscoveryObservers.finishBroadcast();
-
- pw.append(prefix).append(tab).append("start discovery requests:").println();
- final int tokenCount = this.mStartedPrinterDiscoveryTokens.size();
- for (int i = 0; i < tokenCount; i++) {
- IBinder token = mStartedPrinterDiscoveryTokens.get(i);
- pw.append(prefix).append(tab).append(tab).append(token.toString()).println();
- }
-
- pw.append(prefix).append(tab).append("tracked printer requests:").println();
- final int trackedPrinters = mStateTrackedPrinters.size();
- for (int i = 0; i < trackedPrinters; i++) {
- PrinterId printer = mStateTrackedPrinters.get(i);
- pw.append(prefix).append(tab).append(tab).append(printer.toString()).println();
- }
-
- pw.append(prefix).append(tab).append("printers:").println();
- final int pritnerCount = mPrinters.size();
- for (int i = 0; i < pritnerCount; i++) {
- PrinterInfo printer = mPrinters.valueAt(i);
- pw.append(prefix).append(tab).append(tab).append(
- printer.toString()).println();
+ writePrinterInfo(mContext, dumpStream, "printer",
+ PrinterDiscoverySessionProto.PRINTER, printer);
}
}
@@ -1933,36 +1845,22 @@
}
}
- public void dumpLocked(PrintWriter pw, String prefix) {
- String tab = " ";
- final int bucketCount = mPrintJobsForRunningApp.size();
- for (int i = 0; i < bucketCount; i++) {
- final int appId = mPrintJobsForRunningApp.keyAt(i);
- pw.append(prefix).append("appId=" + appId).append(':').println();
- List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i);
- final int printJobCount = bucket.size();
- for (int j = 0; j < printJobCount; j++) {
- PrintJobInfo printJob = bucket.get(j);
- pw.append(prefix).append(tab).append(printJob.toString()).println();
- }
- }
- }
-
- public void dumpLocked(@NonNull ProtoOutputStream proto) {
+ public void dumpLocked(@NonNull DualDumpOutputStream dumpStream) {
final int bucketCount = mPrintJobsForRunningApp.size();
for (int i = 0; i < bucketCount; i++) {
final int appId = mPrintJobsForRunningApp.keyAt(i);
List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i);
final int printJobCount = bucket.size();
for (int j = 0; j < printJobCount; j++) {
- long token = proto.start(PrintUserStateProto.CACHED_PRINT_JOBS);
+ long token = dumpStream.start("cached_print_jobs",
+ PrintUserStateProto.CACHED_PRINT_JOBS);
- proto.write(CachedPrintJobProto.APP_ID, appId);
+ dumpStream.write("app_id", CachedPrintJobProto.APP_ID, appId);
- writePrintJobInfo(mContext, proto, CachedPrintJobProto.PRINT_JOB,
- bucket.get(j));
+ writePrintJobInfo(mContext, dumpStream, "print_job",
+ CachedPrintJobProto.PRINT_JOB, bucket.get(j));
- proto.end(token);
+ dumpStream.end(token);
}
}
}
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java
deleted file mode 100644
index f9ebd28..0000000
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java
+++ /dev/null
@@ -1,341 +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
- */
-
-package com.android.server.backup;
-
-import static com.android.server.backup.testing.TransportTestUtils.TRANSPORT_NAMES;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.robolectric.Shadows.shadowOf;
-import static org.testng.Assert.expectThrows;
-
-import android.app.backup.BackupManager;
-import android.app.backup.ISelectBackupTransportCallback;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.os.HandlerThread;
-import android.platform.test.annotations.Presubmit;
-import android.provider.Settings;
-
-import com.android.server.backup.testing.ShadowAppBackupUtils;
-import com.android.server.backup.testing.TransportTestUtils;
-import com.android.server.backup.testing.TransportTestUtils.TransportData;
-import com.android.server.backup.transport.TransportNotRegisteredException;
-import com.android.server.testing.FrameworkRobolectricTestRunner;
-import com.android.server.testing.SystemLoaderClasses;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowContextWrapper;
-import org.robolectric.shadows.ShadowLog;
-import org.robolectric.shadows.ShadowLooper;
-import org.robolectric.shadows.ShadowSettings;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-@RunWith(FrameworkRobolectricTestRunner.class)
-@Config(
- manifest = Config.NONE,
- sdk = 26,
- shadows = {ShadowAppBackupUtils.class}
-)
-@SystemLoaderClasses({RefactoredBackupManagerService.class, TransportManager.class})
-@Presubmit
-public class BackupManagerServiceRoboTest {
- private static final String TAG = "BMSTest";
- private static final String TRANSPORT_NAME =
- "com.google.android.gms/.backup.BackupTransportService";
-
- @Mock private TransportManager mTransportManager;
- private HandlerThread mBackupThread;
- private ShadowLooper mShadowBackupLooper;
- private File mBaseStateDir;
- private File mDataDir;
- private RefactoredBackupManagerService mBackupManagerService;
- private ShadowContextWrapper mShadowContext;
- private Context mContext;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- mBackupThread = new HandlerThread("backup-test");
- mBackupThread.setUncaughtExceptionHandler(
- (t, e) -> ShadowLog.e(TAG, "Uncaught exception in test thread " + t.getName(), e));
- mBackupThread.start();
- mShadowBackupLooper = shadowOf(mBackupThread.getLooper());
-
- ContextWrapper context = RuntimeEnvironment.application;
- mContext = context;
- mShadowContext = shadowOf(context);
-
- File cacheDir = mContext.getCacheDir();
- mBaseStateDir = new File(cacheDir, "base_state_dir");
- mDataDir = new File(cacheDir, "data_dir");
-
- mBackupManagerService =
- new RefactoredBackupManagerService(
- mContext,
- new Trampoline(mContext),
- mBackupThread,
- mBaseStateDir,
- mDataDir,
- mTransportManager);
- }
-
- @After
- public void tearDown() throws Exception {
- mBackupThread.quit();
- ShadowAppBackupUtils.reset();
- }
-
- /* Tests for destination string */
-
- @Test
- public void testDestinationString() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.getTransportCurrentDestinationString(eq(TRANSPORT_NAME)))
- .thenReturn("destinationString");
-
- String destination = mBackupManagerService.getDestinationString(TRANSPORT_NAME);
-
- assertThat(destination).isEqualTo("destinationString");
- }
-
- @Test
- public void testDestinationString_whenTransportNotRegistered() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.getTransportCurrentDestinationString(eq(TRANSPORT_NAME)))
- .thenThrow(TransportNotRegisteredException.class);
-
- String destination = mBackupManagerService.getDestinationString(TRANSPORT_NAME);
-
- assertThat(destination).isNull();
- }
-
- @Test
- public void testDestinationString_withoutPermission() throws Exception {
- mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.getTransportCurrentDestinationString(eq(TRANSPORT_NAME)))
- .thenThrow(TransportNotRegisteredException.class);
-
- expectThrows(
- SecurityException.class,
- () -> mBackupManagerService.getDestinationString(TRANSPORT_NAME));
- }
-
- /* Tests for app eligibility */
-
- @Test
- public void testIsAppEligibleForBackup_whenAppEligible() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- TransportData transport =
- TransportTestUtils.setUpCurrentTransport(mTransportManager, TRANSPORT_NAME);
- ShadowAppBackupUtils.sAppIsRunningAndEligibleForBackupWithTransport = p -> true;
-
- boolean result = mBackupManagerService.isAppEligibleForBackup("app.package");
-
- assertThat(result).isTrue();
- verify(mTransportManager)
- .disposeOfTransportClient(eq(transport.transportClientMock), any());
- }
-
- @Test
- public void testIsAppEligibleForBackup_whenAppNotEligible() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- TransportTestUtils.setUpCurrentTransport(mTransportManager, TRANSPORT_NAME);
- ShadowAppBackupUtils.sAppIsRunningAndEligibleForBackupWithTransport = p -> false;
-
- boolean result = mBackupManagerService.isAppEligibleForBackup("app.package");
-
- assertThat(result).isFalse();
- }
-
- @Test
- public void testIsAppEligibleForBackup_withoutPermission() throws Exception {
- mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- TransportTestUtils.setUpCurrentTransport(mTransportManager, TRANSPORT_NAME);
-
- expectThrows(
- SecurityException.class,
- () -> mBackupManagerService.isAppEligibleForBackup("app.package"));
- }
-
- @Test
- public void testFilterAppsEligibleForBackup() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- TransportData transport =
- TransportTestUtils.setUpCurrentTransport(mTransportManager, TRANSPORT_NAME);
- Map<String, Boolean> packagesMap = new HashMap<>();
- packagesMap.put("package.a", true);
- packagesMap.put("package.b", false);
- ShadowAppBackupUtils.sAppIsRunningAndEligibleForBackupWithTransport = packagesMap::get;
- String[] packages = packagesMap.keySet().toArray(new String[packagesMap.size()]);
-
- String[] filtered = mBackupManagerService.filterAppsEligibleForBackup(packages);
-
- assertThat(filtered).asList().containsExactly("package.a");
- verify(mTransportManager)
- .disposeOfTransportClient(eq(transport.transportClientMock), any());
- }
-
- @Test
- public void testFilterAppsEligibleForBackup_whenNoneIsEligible() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- ShadowAppBackupUtils.sAppIsRunningAndEligibleForBackupWithTransport = p -> false;
-
- String[] filtered =
- mBackupManagerService.filterAppsEligibleForBackup(
- new String[] {"package.a", "package.b"});
-
- assertThat(filtered).isEmpty();
- }
-
- @Test
- public void testFilterAppsEligibleForBackup_withoutPermission() throws Exception {
- mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- TransportTestUtils.setUpCurrentTransport(mTransportManager, TRANSPORT_NAME);
-
- expectThrows(
- SecurityException.class,
- () ->
- mBackupManagerService.filterAppsEligibleForBackup(
- new String[] {"package.a", "package.b"}));
- }
-
- /* Tests for select transport */
-
- private TransportData mNewTransport;
- private TransportData mOldTransport;
- private ComponentName mNewTransportComponent;
- private ISelectBackupTransportCallback mCallback;
-
- private void setUpForSelectTransport() throws Exception {
- List<TransportData> transports =
- TransportTestUtils.setUpTransports(mTransportManager, TRANSPORT_NAMES);
- mNewTransport = transports.get(0);
- mNewTransportComponent = mNewTransport.transportClientMock.getTransportComponent();
- mOldTransport = transports.get(1);
- when(mTransportManager.selectTransport(eq(mNewTransport.transportName)))
- .thenReturn(mOldTransport.transportName);
- }
-
- @Test
- public void testSelectBackupTransport() throws Exception {
- setUpForSelectTransport();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-
- String oldTransport =
- mBackupManagerService.selectBackupTransport(mNewTransport.transportName);
-
- assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
- assertThat(oldTransport).isEqualTo(mOldTransport.transportName);
- }
-
- @Test
- public void testSelectBackupTransport_withoutPermission() throws Exception {
- setUpForSelectTransport();
- mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-
- expectThrows(
- SecurityException.class,
- () -> mBackupManagerService.selectBackupTransport(mNewTransport.transportName));
- }
-
- @Test
- public void testSelectBackupTransportAsync() throws Exception {
- setUpForSelectTransport();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
- .thenReturn(BackupManager.SUCCESS);
- ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
- mBackupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
-
- mShadowBackupLooper.runToEndOfTasks();
- assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
- verify(callback).onSuccess(eq(mNewTransport.transportName));
- }
-
- @Test
- public void testSelectBackupTransportAsync_whenRegistrationFails() throws Exception {
- setUpForSelectTransport();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
- .thenReturn(BackupManager.ERROR_TRANSPORT_UNAVAILABLE);
- ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
- mBackupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
-
- mShadowBackupLooper.runToEndOfTasks();
- assertThat(getSettingsTransport()).isNotEqualTo(mNewTransport.transportName);
- verify(callback).onFailure(anyInt());
- }
-
- @Test
- public void testSelectBackupTransportAsync_whenTransportGetsUnregistered() throws Exception {
- TransportTestUtils.setUpTransports(
- mTransportManager, new TransportData(TRANSPORT_NAME, null, null));
- ComponentName newTransportComponent =
- TransportTestUtils.transportComponentName(TRANSPORT_NAME);
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.registerAndSelectTransport(eq(newTransportComponent)))
- .thenReturn(BackupManager.SUCCESS);
- ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
- mBackupManagerService.selectBackupTransportAsync(newTransportComponent, callback);
-
- mShadowBackupLooper.runToEndOfTasks();
- assertThat(getSettingsTransport()).isNotEqualTo(TRANSPORT_NAME);
- verify(callback).onFailure(anyInt());
- }
-
- @Test
- public void testSelectBackupTransportAsync_withoutPermission() throws Exception {
- setUpForSelectTransport();
- mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- ComponentName newTransportComponent =
- mNewTransport.transportClientMock.getTransportComponent();
-
- expectThrows(
- SecurityException.class,
- () ->
- mBackupManagerService.selectBackupTransportAsync(
- newTransportComponent, mock(ISelectBackupTransportCallback.class)));
- }
-
- private String getSettingsTransport() {
- return ShadowSettings.ShadowSecure.getString(
- mContext.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
- }
-}
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
new file mode 100644
index 0000000..e072800
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -0,0 +1,679 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup;
+
+import static com.android.server.backup.testing.TransportData.backupTransport;
+import static com.android.server.backup.testing.TransportData.d2dTransport;
+import static com.android.server.backup.testing.TransportData.localTransport;
+import static com.android.server.backup.testing.TransportTestUtils.setUpCurrentTransport;
+import static com.android.server.backup.testing.TransportTestUtils.setUpTransports;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
+import static org.testng.Assert.expectThrows;
+
+import android.app.backup.BackupManager;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.os.HandlerThread;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+import com.android.server.backup.testing.ShadowAppBackupUtils;
+import com.android.server.backup.testing.ShadowBackupPolicyEnforcer;
+import com.android.server.backup.testing.TransportData;
+import com.android.server.backup.testing.TransportTestUtils.TransportMock;
+import com.android.server.backup.transport.TransportNotRegisteredException;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderClasses;
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowContextWrapper;
+import org.robolectric.shadows.ShadowLog;
+import org.robolectric.shadows.ShadowLooper;
+import org.robolectric.shadows.ShadowPackageManager;
+import org.robolectric.shadows.ShadowSettings;
+import org.robolectric.shadows.ShadowSystemClock;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(
+ manifest = Config.NONE,
+ sdk = 26,
+ shadows = {ShadowAppBackupUtils.class, ShadowBackupPolicyEnforcer.class}
+)
+@SystemLoaderClasses({RefactoredBackupManagerService.class, TransportManager.class})
+@Presubmit
+public class BackupManagerServiceTest {
+ private static final String TAG = "BMSTest";
+
+ @Mock private TransportManager mTransportManager;
+ private HandlerThread mBackupThread;
+ private ShadowLooper mShadowBackupLooper;
+ private File mBaseStateDir;
+ private File mDataDir;
+ private ShadowContextWrapper mShadowContext;
+ private Context mContext;
+ private TransportData mTransport;
+ private String mTransportName;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mTransport = backupTransport();
+ mTransportName = mTransport.transportName;
+
+ mBackupThread = new HandlerThread("backup-test");
+ mBackupThread.setUncaughtExceptionHandler(
+ (t, e) -> ShadowLog.e(TAG, "Uncaught exception in test thread " + t.getName(), e));
+ mBackupThread.start();
+ mShadowBackupLooper = shadowOf(mBackupThread.getLooper());
+
+ ContextWrapper context = RuntimeEnvironment.application;
+ mContext = context;
+ mShadowContext = shadowOf(context);
+
+ File cacheDir = mContext.getCacheDir();
+ mBaseStateDir = new File(cacheDir, "base_state_dir");
+ mDataDir = new File(cacheDir, "data_dir");
+
+ ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(null);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mBackupThread.quit();
+ ShadowAppBackupUtils.reset();
+ ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(null);
+ }
+
+ /* Tests for destination string */
+
+ @Test
+ public void testDestinationString() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
+ .thenReturn("destinationString");
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ String destination = backupManagerService.getDestinationString(mTransportName);
+
+ assertThat(destination).isEqualTo("destinationString");
+ }
+
+ @Test
+ public void testDestinationString_whenTransportNotRegistered() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
+ .thenThrow(TransportNotRegisteredException.class);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ String destination = backupManagerService.getDestinationString(mTransportName);
+
+ assertThat(destination).isNull();
+ }
+
+ @Test
+ public void testDestinationString_withoutPermission() throws Exception {
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
+ .thenThrow(TransportNotRegisteredException.class);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () -> backupManagerService.getDestinationString(mTransportName));
+ }
+
+ /* Tests for app eligibility */
+
+ @Test
+ public void testIsAppEligibleForBackup_whenAppEligible() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ TransportMock transportMock = setUpCurrentTransport(mTransportManager, backupTransport());
+ ShadowAppBackupUtils.sAppIsRunningAndEligibleForBackupWithTransport = p -> true;
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ boolean result = backupManagerService.isAppEligibleForBackup("app.package");
+
+ assertThat(result).isTrue();
+
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(transportMock.transportClient), any());
+ }
+
+ @Test
+ public void testIsAppEligibleForBackup_whenAppNotEligible() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ setUpCurrentTransport(mTransportManager, mTransport);
+ ShadowAppBackupUtils.sAppIsRunningAndEligibleForBackupWithTransport = p -> false;
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ boolean result = backupManagerService.isAppEligibleForBackup("app.package");
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void testIsAppEligibleForBackup_withoutPermission() throws Exception {
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ setUpCurrentTransport(mTransportManager, mTransport);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () -> backupManagerService.isAppEligibleForBackup("app.package"));
+ }
+
+ @Test
+ public void testFilterAppsEligibleForBackup() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ TransportMock transportMock = setUpCurrentTransport(mTransportManager, mTransport);
+ Map<String, Boolean> packagesMap = new HashMap<>();
+ packagesMap.put("package.a", true);
+ packagesMap.put("package.b", false);
+ ShadowAppBackupUtils.sAppIsRunningAndEligibleForBackupWithTransport = packagesMap::get;
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+ String[] packages = packagesMap.keySet().toArray(new String[packagesMap.size()]);
+
+ String[] filtered = backupManagerService.filterAppsEligibleForBackup(packages);
+
+ assertThat(filtered).asList().containsExactly("package.a");
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(transportMock.transportClient), any());
+ }
+
+ @Test
+ public void testFilterAppsEligibleForBackup_whenNoneIsEligible() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ ShadowAppBackupUtils.sAppIsRunningAndEligibleForBackupWithTransport = p -> false;
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ String[] filtered =
+ backupManagerService.filterAppsEligibleForBackup(
+ new String[] {"package.a", "package.b"});
+
+ assertThat(filtered).isEmpty();
+ }
+
+ @Test
+ public void testFilterAppsEligibleForBackup_withoutPermission() throws Exception {
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ setUpCurrentTransport(mTransportManager, mTransport);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.filterAppsEligibleForBackup(
+ new String[] {"package.a", "package.b"}));
+ }
+
+ /* Tests for select transport */
+
+ private ComponentName mNewTransportComponent;
+ private TransportData mNewTransport;
+ private TransportMock mNewTransportMock;
+ private ComponentName mOldTransportComponent;
+ private TransportData mOldTransport;
+ private TransportMock mOldTransportMock;
+
+ private void setUpForSelectTransport() throws Exception {
+ mNewTransport = backupTransport();
+ mNewTransportComponent = mNewTransport.getTransportComponent();
+ mOldTransport = d2dTransport();
+ mOldTransportComponent = mOldTransport.getTransportComponent();
+ List<TransportMock> transportMocks =
+ setUpTransports(mTransportManager, mNewTransport, mOldTransport, localTransport());
+ mNewTransportMock = transportMocks.get(0);
+ mOldTransportMock = transportMocks.get(1);
+ when(mTransportManager.selectTransport(eq(mNewTransport.transportName)))
+ .thenReturn(mOldTransport.transportName);
+ }
+
+ @Test
+ public void testSelectBackupTransport() throws Exception {
+ setUpForSelectTransport();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ String oldTransport =
+ backupManagerService.selectBackupTransport(mNewTransport.transportName);
+
+ assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
+ assertThat(oldTransport).isEqualTo(mOldTransport.transportName);
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
+ }
+
+ @Test
+ public void testSelectBackupTransport_withoutPermission() throws Exception {
+ setUpForSelectTransport();
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () -> backupManagerService.selectBackupTransport(mNewTransport.transportName));
+ }
+
+ @Test
+ public void testSelectBackupTransportAsync() throws Exception {
+ setUpForSelectTransport();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
+ .thenReturn(BackupManager.SUCCESS);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+ backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
+
+ mShadowBackupLooper.runToEndOfTasks();
+ assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
+ verify(callback).onSuccess(eq(mNewTransport.transportName));
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
+ }
+
+ @Test
+ public void testSelectBackupTransportAsync_whenMandatoryTransport() throws Exception {
+ setUpForSelectTransport();
+ ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(mNewTransportComponent);
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
+ .thenReturn(BackupManager.SUCCESS);
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
+
+ mShadowBackupLooper.runToEndOfTasks();
+ assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
+ verify(callback).onSuccess(eq(mNewTransport.transportName));
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
+ }
+
+ @Test
+ public void testSelectBackupTransportAsync_whenOtherThanMandatoryTransport() throws Exception {
+ setUpForSelectTransport();
+ ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(mOldTransportComponent);
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
+ .thenReturn(BackupManager.SUCCESS);
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
+
+ mShadowBackupLooper.runToEndOfTasks();
+ assertThat(getSettingsTransport()).isNotEqualTo(mNewTransport.transportName);
+ verify(callback).onFailure(eq(BackupManager.ERROR_BACKUP_NOT_ALLOWED));
+ }
+
+ @Test
+ public void testSelectBackupTransportAsync_whenRegistrationFails() throws Exception {
+ setUpForSelectTransport();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
+ .thenReturn(BackupManager.ERROR_TRANSPORT_UNAVAILABLE);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+ backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
+
+ mShadowBackupLooper.runToEndOfTasks();
+ assertThat(getSettingsTransport()).isNotEqualTo(mNewTransport.transportName);
+ verify(callback).onFailure(anyInt());
+ }
+
+ @Test
+ public void testSelectBackupTransportAsync_whenTransportGetsUnregistered() throws Exception {
+ setUpTransports(mTransportManager, mTransport.unregistered());
+ ComponentName newTransportComponent = mTransport.getTransportComponent();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.registerAndSelectTransport(eq(newTransportComponent)))
+ .thenReturn(BackupManager.SUCCESS);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+ backupManagerService.selectBackupTransportAsync(newTransportComponent, callback);
+
+ mShadowBackupLooper.runToEndOfTasks();
+ assertThat(getSettingsTransport()).isNotEqualTo(mTransportName);
+ verify(callback).onFailure(anyInt());
+ }
+
+ @Test
+ public void testSelectBackupTransportAsync_withoutPermission() throws Exception {
+ setUpForSelectTransport();
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+ ComponentName newTransportComponent = mNewTransport.getTransportComponent();
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.selectBackupTransportAsync(
+ newTransportComponent, mock(ISelectBackupTransportCallback.class)));
+ }
+
+ private String getSettingsTransport() {
+ return ShadowSettings.ShadowSecure.getString(
+ mContext.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
+ }
+
+ /* Tests for updating transport attributes */
+
+ private static final int PACKAGE_UID = 10;
+ private ComponentName mTransportComponent;
+ private int mTransportUid;
+
+ private void setUpForUpdateTransportAttributes() throws Exception {
+ mTransportComponent = mTransport.getTransportComponent();
+ String transportPackage = mTransportComponent.getPackageName();
+
+ ShadowPackageManager shadowPackageManager = shadowOf(mContext.getPackageManager());
+ shadowPackageManager.addPackage(transportPackage);
+ shadowPackageManager.setPackagesForUid(PACKAGE_UID, transportPackage);
+
+ mTransportUid = mContext.getPackageManager().getPackageUid(transportPackage, 0);
+ }
+
+ @Test
+ public void
+ testUpdateTransportAttributes_whenTransportUidEqualsToCallingUid_callsThroughToTransportManager()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ Intent configurationIntent = new Intent();
+ Intent dataManagementIntent = new Intent();
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+
+ verify(mTransportManager)
+ .updateTransportAttributes(
+ eq(mTransportComponent),
+ eq(mTransportName),
+ eq(configurationIntent),
+ eq("currentDestinationString"),
+ eq(dataManagementIntent),
+ eq("dataManagementLabel"));
+ }
+
+ @Test
+ public void testUpdateTransportAttributes_whenTransportUidNotEqualToCallingUid_throwsException()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid + 1,
+ mTransportComponent,
+ mTransportName,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ "dataManagementLabel"));
+ }
+
+ @Test
+ public void testUpdateTransportAttributes_whenTransportComponentNull_throwsException()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ expectThrows(
+ RuntimeException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ null,
+ mTransportName,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ "dataManagementLabel"));
+ }
+
+ @Test
+ public void testUpdateTransportAttributes_whenNameNull_throwsException() throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ expectThrows(
+ RuntimeException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ null,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ "dataManagementLabel"));
+ }
+
+ @Test
+ public void testUpdateTransportAttributes_whenCurrentDestinationStringNull_throwsException()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ expectThrows(
+ RuntimeException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ new Intent(),
+ null,
+ new Intent(),
+ "dataManagementLabel"));
+ }
+
+ @Test
+ public void
+ testUpdateTransportAttributes_whenDataManagementArgumentsNullityDontMatch_throwsException()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ expectThrows(
+ RuntimeException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ new Intent(),
+ "currentDestinationString",
+ null,
+ "dataManagementLabel"));
+
+ expectThrows(
+ RuntimeException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ null));
+ }
+
+ @Test
+ public void testUpdateTransportAttributes_whenPermissionGranted_callsThroughToTransportManager()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ Intent configurationIntent = new Intent();
+ Intent dataManagementIntent = new Intent();
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+
+ verify(mTransportManager)
+ .updateTransportAttributes(
+ eq(mTransportComponent),
+ eq(mTransportName),
+ eq(configurationIntent),
+ eq("currentDestinationString"),
+ eq(dataManagementIntent),
+ eq("dataManagementLabel"));
+ }
+
+ @Test
+ public void testUpdateTransportAttributes_whenPermissionDenied_throwsSecurityException()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ RefactoredBackupManagerService backupManagerService =
+ createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ "dataManagementLabel"));
+ }
+
+ /* Miscellaneous tests */
+
+ @Test
+ public void testConstructor_postRegisterTransports() {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+
+ createBackupManagerService();
+
+ mShadowBackupLooper.runToEndOfTasks();
+ verify(mTransportManager).registerTransports();
+ }
+
+ @Test
+ public void testConstructor_doesNotRegisterTransportsSynchronously() {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+
+ createBackupManagerService();
+
+ // Operations posted to mBackupThread only run with mShadowBackupLooper.runToEndOfTasks()
+ verify(mTransportManager, never()).registerTransports();
+ }
+
+ private RefactoredBackupManagerService createBackupManagerService() {
+ return new RefactoredBackupManagerService(
+ mContext,
+ new Trampoline(mContext),
+ mBackupThread,
+ mBaseStateDir,
+ mDataDir,
+ mTransportManager);
+ }
+
+ private RefactoredBackupManagerService createInitializedBackupManagerService() {
+ RefactoredBackupManagerService backupManagerService =
+ new RefactoredBackupManagerService(
+ mContext,
+ new Trampoline(mContext),
+ mBackupThread,
+ mBaseStateDir,
+ mDataDir,
+ mTransportManager);
+ mShadowBackupLooper.runToEndOfTasks();
+ // Handler instances have their own clock, so advancing looper (with runToEndOfTasks())
+ // above does NOT advance the handlers' clock, hence whenever a handler post messages with
+ // specific time to the looper the time of those messages will be before the looper's time.
+ // To fix this we advance SystemClock as well since that is from where the handlers read
+ // time.
+ ShadowSystemClock.setCurrentTimeMillis(mShadowBackupLooper.getScheduler().getCurrentTime());
+ return backupManagerService;
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
index acd670f..cf0bc23 100644
--- a/services/robotests/src/com/android/server/backup/TransportManagerTest.java
+++ b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
@@ -16,108 +16,97 @@
package com.android.server.backup;
+import static com.android.server.backup.testing.TransportData.genericTransport;
+import static com.android.server.backup.testing.TransportTestUtils.mockTransport;
+import static com.android.server.backup.testing.TransportTestUtils.setUpTransportsForTransportManager;
+
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.mock;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.shadow.api.Shadow.extract;
import static org.testng.Assert.expectThrows;
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+import static java.util.stream.Stream.concat;
+
import android.annotation.Nullable;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.os.IBinder;
-import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
-import com.android.internal.backup.IBackupTransport;
-import com.android.server.backup.testing.ShadowBackupTransportStub;
import com.android.server.backup.testing.ShadowContextImplForBackup;
-import com.android.server.backup.testing.ShadowPackageManagerForBackup;
-import com.android.server.backup.testing.TransportBoundListenerStub;
+import com.android.server.testing.shadows.FrameworkShadowPackageManager;
+import com.android.server.backup.testing.TransportData;
+import com.android.server.backup.testing.TransportTestUtils.TransportMock;
+import com.android.server.backup.transport.OnTransportRegisteredListener;
import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportClientManager;
import com.android.server.backup.transport.TransportNotRegisteredException;
import com.android.server.testing.FrameworkRobolectricTestRunner;
import com.android.server.testing.SystemLoaderClasses;
+import com.android.server.testing.shadows.FrameworkShadowContextImpl;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowLog;
-import org.robolectric.shadows.ShadowLooper;
import org.robolectric.shadows.ShadowPackageManager;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Stream;
@RunWith(FrameworkRobolectricTestRunner.class)
@Config(
- manifest = Config.NONE,
- sdk = 26,
- shadows = {
- ShadowContextImplForBackup.class,
- ShadowBackupTransportStub.class,
- ShadowPackageManagerForBackup.class
- }
+ manifest = Config.NONE,
+ sdk = 26,
+ shadows = {FrameworkShadowPackageManager.class, FrameworkShadowContextImpl.class}
)
@SystemLoaderClasses({TransportManager.class})
@Presubmit
public class TransportManagerTest {
- private static final String PACKAGE_NAME = "some.package.name";
- private static final String ANOTHER_PACKAGE_NAME = "another.package.name";
+ private static final String PACKAGE_A = "some.package.a";
+ private static final String PACKAGE_B = "some.package.b";
- private TransportInfo mTransport1;
- private TransportInfo mTransport2;
+ @Mock private OnTransportRegisteredListener mListener;
+ @Mock private TransportClientManager mTransportClientManager;
+ private TransportData mTransportA1;
+ private TransportData mTransportA2;
+ private TransportData mTransportB1;
- private ShadowPackageManager mPackageManagerShadow;
-
- private final TransportBoundListenerStub mTransportBoundListenerStub =
- new TransportBoundListenerStub(true);
+ private ShadowPackageManager mShadowPackageManager;
+ private Context mContext;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- ShadowLog.stream = System.out;
-
- mPackageManagerShadow =
- (ShadowPackageManagerForBackup)
+ mShadowPackageManager =
+ (FrameworkShadowPackageManager)
extract(RuntimeEnvironment.application.getPackageManager());
+ mContext = RuntimeEnvironment.application.getApplicationContext();
- mTransport1 = new TransportInfo(
- PACKAGE_NAME,
- "transport1.name",
- new Intent(),
- "currentDestinationString",
- new Intent(),
- "dataManagementLabel");
- mTransport2 = new TransportInfo(
- PACKAGE_NAME,
- "transport2.name",
- new Intent(),
- "currentDestinationString",
- new Intent(),
- "dataManagementLabel");
-
- ShadowContextImplForBackup.sComponentBinderMap.put(mTransport1.componentName,
- mTransport1.binder);
- ShadowContextImplForBackup.sComponentBinderMap.put(mTransport2.componentName,
- mTransport2.binder);
- ShadowBackupTransportStub.sBinderTransportMap.put(
- mTransport1.binder, mTransport1.binderInterface);
- ShadowBackupTransportStub.sBinderTransportMap.put(
- mTransport2.binder, mTransport2.binderInterface);
+ mTransportA1 = genericTransport(PACKAGE_A, "TransportFoo");
+ mTransportA2 = genericTransport(PACKAGE_A, "TransportBar");
+ mTransportB1 = genericTransport(PACKAGE_B, "TransportBaz");
}
@After
@@ -126,560 +115,441 @@
}
@Test
- public void onPackageAdded_bindsToAllTransports() throws Exception {
- setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2),
- ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
-
- TransportManager transportManager = new TransportManager(
- RuntimeEnvironment.application.getApplicationContext(),
- new HashSet<>(Arrays.asList(
- mTransport1.componentName, mTransport2.componentName)),
- null /* defaultTransport */,
- mTransportBoundListenerStub,
- ShadowLooper.getMainLooper());
- transportManager.onPackageAdded(PACKAGE_NAME);
-
- assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.componentName, mTransport2.componentName));
- assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.name, mTransport2.name));
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
- .isTrue();
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
- .isTrue();
- }
-
- @Test
- public void onPackageAdded_oneTransportUnavailable_bindsToOnlyOneTransport() throws Exception {
- setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2),
- ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
-
- ShadowContextImplForBackup.sUnbindableComponents.add(mTransport1.componentName);
-
- TransportManager transportManager = new TransportManager(
- RuntimeEnvironment.application.getApplicationContext(),
- new HashSet<>(Arrays.asList(
- mTransport1.componentName, mTransport2.componentName)),
- null /* defaultTransport */,
- mTransportBoundListenerStub,
- ShadowLooper.getMainLooper());
- transportManager.onPackageAdded(PACKAGE_NAME);
-
- assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
- Collections.singleton(mTransport2.componentName));
- assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
- Collections.singleton(mTransport2.name));
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
- .isFalse();
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
- .isTrue();
- }
-
- @Test
- public void onPackageAdded_whitelistIsNull_doesNotBindToTransports() throws Exception {
- setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2),
- ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
-
- TransportManager transportManager = new TransportManager(
- RuntimeEnvironment.application.getApplicationContext(),
- null /* whitelist */,
- null /* defaultTransport */,
- mTransportBoundListenerStub,
- ShadowLooper.getMainLooper());
- transportManager.onPackageAdded(PACKAGE_NAME);
-
- assertThat(transportManager.getAllTransportComponents()).isEmpty();
- assertThat(transportManager.getBoundTransportNames()).isEmpty();
- assertThat(mTransportBoundListenerStub.isCalled()).isFalse();
- }
-
- @Test
- public void onPackageAdded_onlyOneTransportWhitelisted_onlyConnectsToWhitelistedTransport()
- throws Exception {
- setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2),
- ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
-
- TransportManager transportManager = new TransportManager(
- RuntimeEnvironment.application.getApplicationContext(),
- new HashSet<>(Collections.singleton(mTransport2.componentName)),
- null /* defaultTransport */,
- mTransportBoundListenerStub,
- ShadowLooper.getMainLooper());
- transportManager.onPackageAdded(PACKAGE_NAME);
-
- assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
- Collections.singleton(mTransport2.componentName));
- assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
- Collections.singleton(mTransport2.name));
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
- .isFalse();
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
- .isTrue();
- }
-
- @Test
- public void onPackageAdded_appIsNotPrivileged_doesNotBindToTransports() throws Exception {
- setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2), 0);
-
- TransportManager transportManager = new TransportManager(
- RuntimeEnvironment.application.getApplicationContext(),
- new HashSet<>(Arrays.asList(
- mTransport1.componentName, mTransport2.componentName)),
- null /* defaultTransport */,
- mTransportBoundListenerStub,
- ShadowLooper.getMainLooper());
- transportManager.onPackageAdded(PACKAGE_NAME);
-
- assertThat(transportManager.getAllTransportComponents()).isEmpty();
- assertThat(transportManager.getBoundTransportNames()).isEmpty();
- assertThat(mTransportBoundListenerStub.isCalled()).isFalse();
- }
-
- @Test
- public void onPackageRemoved_transportsUnbound() throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
-
- transportManager.onPackageRemoved(PACKAGE_NAME);
-
- assertThat(transportManager.getAllTransportComponents()).isEmpty();
- assertThat(transportManager.getBoundTransportNames()).isEmpty();
- }
-
- @Test
- public void onPackageRemoved_incorrectPackageName_nothingHappens() throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
-
- transportManager.onPackageRemoved(ANOTHER_PACKAGE_NAME);
-
- assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.componentName, mTransport2.componentName));
- assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.name, mTransport2.name));
- }
-
- @Test
- public void onPackageChanged_oneComponentChanged_onlyOneTransportRebound() throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
-
- transportManager.onPackageChanged(PACKAGE_NAME, new String[]{mTransport2.name});
-
- assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.componentName, mTransport2.componentName));
- assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.name, mTransport2.name));
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
- .isFalse();
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
- .isTrue();
- }
-
- @Test
- public void onPackageChanged_nothingChanged_noTransportsRebound() throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
-
- transportManager.onPackageChanged(PACKAGE_NAME, new String[0]);
-
- assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.componentName, mTransport2.componentName));
- assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.name, mTransport2.name));
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
- .isFalse();
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
- .isFalse();
- }
-
- @Test
- public void onPackageChanged_unexpectedComponentChanged_noTransportsRebound() throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
-
- transportManager.onPackageChanged(PACKAGE_NAME, new String[]{"unexpected.component"});
-
- assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.componentName, mTransport2.componentName));
- assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.name, mTransport2.name));
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
- .isFalse();
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
- .isFalse();
- }
-
- @Test
- public void onPackageChanged_transportsRebound() throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
-
- transportManager.onPackageChanged(PACKAGE_NAME, new String[]{mTransport2.name});
-
- assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.componentName, mTransport2.componentName));
- assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
- Arrays.asList(mTransport1.name, mTransport2.name));
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
- .isFalse();
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
- .isTrue();
- }
-
- @Test
- public void getTransportBinder_returnsCorrectBinder() throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
-
- assertThat(transportManager.getTransportBinder(mTransport1.name)).isEqualTo(
- mTransport1.binderInterface);
- assertThat(transportManager.getTransportBinder(mTransport2.name)).isEqualTo(
- mTransport2.binderInterface);
- }
-
- @Test
- public void getTransportBinder_incorrectTransportName_returnsNull() throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
-
- assertThat(transportManager.getTransportBinder("incorrect.transport")).isNull();
- }
-
- @Test
- public void getTransportBinder_oneTransportUnavailable_returnsCorrectBinder() throws Exception {
+ public void testRegisterTransports() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpPackage(PACKAGE_B, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2, mTransportB1);
TransportManager transportManager =
- createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2),
- Collections.singletonList(mTransport1), mTransport1.name);
+ createTransportManager(mTransportA1, mTransportA2, mTransportB1);
- assertThat(transportManager.getTransportBinder(mTransport1.name)).isNull();
- assertThat(transportManager.getTransportBinder(mTransport2.name)).isEqualTo(
- mTransport2.binderInterface);
+ transportManager.registerTransports();
+
+ assertRegisteredTransports(
+ transportManager, asList(mTransportA1, mTransportA2, mTransportB1));
+
+ verify(mListener)
+ .onTransportRegistered(mTransportA1.transportName, mTransportA1.transportDirName);
+ verify(mListener)
+ .onTransportRegistered(mTransportA2.transportName, mTransportA2.transportDirName);
+ verify(mListener)
+ .onTransportRegistered(mTransportB1.transportName, mTransportB1.transportDirName);
}
@Test
- public void getCurrentTransport_selectTransportNotCalled_returnsDefaultTransport()
+ public void
+ testRegisterTransports_whenOneTransportUnavailable_doesNotRegisterUnavailableTransport()
+ throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ TransportData transport1 = mTransportA1.unavailable();
+ TransportData transport2 = mTransportA2;
+ setUpTransports(transport1, transport2);
+ TransportManager transportManager = createTransportManager(transport1, transport2);
+
+ transportManager.registerTransports();
+
+ assertRegisteredTransports(transportManager, singletonList(transport2));
+ verify(mListener, never())
+ .onTransportRegistered(transport1.transportName, transport1.transportDirName);
+ verify(mListener)
+ .onTransportRegistered(transport2.transportName, transport2.transportDirName);
+ }
+
+ @Test
+ public void testRegisterTransports_whenWhitelistIsEmpty_doesNotRegisterTransports()
throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(null);
- assertThat(transportManager.getCurrentTransportName()).isEqualTo(mTransport1.name);
+ transportManager.registerTransports();
+
+ assertRegisteredTransports(transportManager, emptyList());
+ verify(mListener, never()).onTransportRegistered(any(), any());
}
@Test
- public void getCurrentTransport_selectTransportCalled_returnsCorrectTransport()
+ public void
+ testRegisterTransports_whenOnlyOneTransportWhitelisted_onlyRegistersWhitelistedTransport()
+ throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(null, mTransportA1);
+
+ transportManager.registerTransports();
+
+ assertRegisteredTransports(transportManager, singletonList(mTransportA1));
+ verify(mListener)
+ .onTransportRegistered(mTransportA1.transportName, mTransportA1.transportDirName);
+ verify(mListener, never())
+ .onTransportRegistered(mTransportA2.transportName, mTransportA2.transportDirName);
+ }
+
+ @Test
+ public void testRegisterTransports_whenAppIsNotPrivileged_doesNotRegisterTransports()
throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
-
- assertThat(transportManager.getCurrentTransportName()).isEqualTo(mTransport1.name);
-
- transportManager.selectTransport(mTransport2.name);
-
- assertThat(transportManager.getCurrentTransportName()).isEqualTo(mTransport2.name);
- }
-
- @Test
- public void getCurrentTransportBinder_returnsCorrectBinder() throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
-
- assertThat(transportManager.getCurrentTransportBinder())
- .isEqualTo(mTransport1.binderInterface);
- }
-
- @Test
- public void getCurrentTransportBinder_transportNotBound_returnsNull() throws Exception {
+ // Note ApplicationInfo.PRIVATE_FLAG_PRIVILEGED is missing from flags
+ setUpPackage(PACKAGE_A, 0);
+ setUpTransports(mTransportA1, mTransportA2);
TransportManager transportManager =
- createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2),
- Collections.singletonList(mTransport1), mTransport2.name);
+ createTransportManager(null, mTransportA1, mTransportA2);
- transportManager.selectTransport(mTransport1.name);
+ transportManager.registerTransports();
- assertThat(transportManager.getCurrentTransportBinder()).isNull();
+ assertRegisteredTransports(transportManager, emptyList());
+ verify(mListener, never()).onTransportRegistered(any(), any());
}
@Test
- public void getTransportName_returnsCorrectTransportName() throws Exception {
- TransportManager transportManager = createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+ public void testOnPackageAdded_registerTransports() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1);
+ TransportManager transportManager = createTransportManager(mTransportA1);
- assertThat(transportManager.getTransportName(mTransport1.binderInterface))
- .isEqualTo(mTransport1.name);
- assertThat(transportManager.getTransportName(mTransport2.binderInterface))
- .isEqualTo(mTransport2.name);
+ transportManager.onPackageAdded(PACKAGE_A);
+
+ assertRegisteredTransports(transportManager, asList(mTransportA1));
+ verify(mListener)
+ .onTransportRegistered(mTransportA1.transportName, mTransportA1.transportDirName);
}
@Test
- public void getTransportName_transportNotBound_returnsNull() throws Exception {
- TransportManager transportManager =
- createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2),
- Collections.singletonList(mTransport1), mTransport1.name);
+ public void testOnPackageRemoved_unregisterTransports() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpPackage(PACKAGE_B, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportB1);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportB1);
+ transportManager.registerTransports();
- assertThat(transportManager.getTransportName(mTransport1.binderInterface)).isNull();
- assertThat(transportManager.getTransportName(mTransport2.binderInterface))
- .isEqualTo(mTransport2.name);
+ transportManager.onPackageRemoved(PACKAGE_A);
+
+ assertRegisteredTransports(transportManager, singletonList(mTransportB1));
}
@Test
- public void getTransportWhitelist_returnsCorrectWhiteList() throws Exception {
- TransportManager transportManager = new TransportManager(
- RuntimeEnvironment.application.getApplicationContext(),
- new HashSet<>(Arrays.asList(mTransport1.componentName, mTransport2.componentName)),
- mTransport1.name,
- mTransportBoundListenerStub,
- ShadowLooper.getMainLooper());
+ public void testOnPackageRemoved_whenUnknownPackage_nothingHappens() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1);
+ TransportManager transportManager = createTransportManager(mTransportA1);
+ transportManager.registerTransports();
- assertThat(transportManager.getTransportWhitelist()).containsExactlyElementsIn(
- Arrays.asList(mTransport1.componentName, mTransport2.componentName));
+ transportManager.onPackageRemoved(PACKAGE_A + "unknown");
+
+ assertRegisteredTransports(transportManager, singletonList(mTransportA1));
}
@Test
- public void getTransportWhitelist_whiteListIsNull_returnsEmptyArray() throws Exception {
- TransportManager transportManager = new TransportManager(
- RuntimeEnvironment.application.getApplicationContext(),
- null /* whitelist */,
- mTransport1.name,
- mTransportBoundListenerStub,
- ShadowLooper.getMainLooper());
-
- assertThat(transportManager.getTransportWhitelist()).isEmpty();
- }
-
- @Test
- public void selectTransport_setsTransportCorrectlyAndReturnsPreviousTransport()
+ public void testOnPackageChanged_whenOneComponentChanged_onlyOneTransportReRegistered()
throws Exception {
- TransportManager transportManager = new TransportManager(
- RuntimeEnvironment.application.getApplicationContext(),
- null /* whitelist */,
- mTransport1.name,
- mTransportBoundListenerStub,
- ShadowLooper.getMainLooper());
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2);
+ transportManager.registerTransports();
+ // Reset listener to verify calls after registerTransports() above
+ reset(mListener);
- assertThat(transportManager.selectTransport(mTransport2.name)).isEqualTo(mTransport1.name);
- assertThat(transportManager.selectTransport(mTransport1.name)).isEqualTo(mTransport2.name);
+ transportManager.onPackageChanged(
+ PACKAGE_A, mTransportA1.getTransportComponent().getClassName());
+
+ assertRegisteredTransports(transportManager, asList(mTransportA1, mTransportA2));
+ verify(mListener)
+ .onTransportRegistered(mTransportA1.transportName, mTransportA1.transportDirName);
+ verify(mListener, never())
+ .onTransportRegistered(mTransportA2.transportName, mTransportA2.transportDirName);
}
@Test
- public void getTransportClient_forRegisteredTransport_returnCorrectly() throws Exception {
+ public void testOnPackageChanged_whenNoComponentsChanged_doesNotRegisterTransports()
+ throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1);
+ TransportManager transportManager = createTransportManager(mTransportA1);
+ transportManager.registerTransports();
+ reset(mListener);
+
+ transportManager.onPackageChanged(PACKAGE_A);
+
+ assertRegisteredTransports(transportManager, singletonList(mTransportA1));
+ verify(mListener, never()).onTransportRegistered(any(), any());
+ }
+
+ @Test
+ public void testOnPackageChanged_whenUnknownComponentChanged_noTransportsRegistered()
+ throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1);
+ TransportManager transportManager = createTransportManager(mTransportA1);
+ transportManager.registerTransports();
+ reset(mListener);
+
+ transportManager.onPackageChanged(PACKAGE_A, PACKAGE_A + ".UnknownComponent");
+
+ assertRegisteredTransports(transportManager, singletonList(mTransportA1));
+ verify(mListener, never()).onTransportRegistered(any(), any());
+ }
+
+ @Test
+ public void testOnPackageChanged_reRegisterTransports() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2);
+ transportManager.registerTransports();
+ reset(mListener);
+
+ transportManager.onPackageChanged(
+ PACKAGE_A,
+ mTransportA1.getTransportComponent().getClassName(),
+ mTransportA2.getTransportComponent().getClassName());
+
+ assertRegisteredTransports(transportManager, asList(mTransportA1, mTransportA2));
+ verify(mListener)
+ .onTransportRegistered(mTransportA1.transportName, mTransportA1.transportDirName);
+ verify(mListener)
+ .onTransportRegistered(mTransportA2.transportName, mTransportA2.transportDirName);
+ }
+
+ @Test
+ public void testGetCurrentTransportName_whenSelectTransportNotCalled_returnsDefaultTransport()
+ throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2);
+ transportManager.registerTransports();
+
+ String currentTransportName = transportManager.getCurrentTransportName();
+
+ assertThat(currentTransportName).isEqualTo(mTransportA1.transportName);
+ }
+
+ @Test
+ public void testGetCurrentTransport_whenSelectTransportCalled_returnsSelectedTransport()
+ throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2);
+ transportManager.registerTransports();
+ transportManager.selectTransport(mTransportA2.transportName);
+
+ String currentTransportName = transportManager.getCurrentTransportName();
+
+ assertThat(currentTransportName).isEqualTo(mTransportA2.transportName);
+ }
+
+ @Test
+ public void testGetTransportWhitelist() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2);
+
+ Set<ComponentName> transportWhitelist = transportManager.getTransportWhitelist();
+
+ assertThat(transportWhitelist)
+ .containsExactlyElementsIn(
+ asList(
+ mTransportA1.getTransportComponent(),
+ mTransportA2.getTransportComponent()));
+ }
+
+ @Test
+ public void testSelectTransport() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
TransportManager transportManager =
- createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+ createTransportManager(null, mTransportA1, mTransportA2);
+
+ String transport1 = transportManager.selectTransport(mTransportA1.transportName);
+ String transport2 = transportManager.selectTransport(mTransportA2.transportName);
+
+ assertThat(transport1).isNull();
+ assertThat(transport2).isEqualTo(mTransportA1.transportName);
+ }
+
+ @Test
+ public void testGetTransportClient_forRegisteredTransport() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2);
+ transportManager.registerTransports();
TransportClient transportClient =
- transportManager.getTransportClient(mTransport1.name, "caller");
+ transportManager.getTransportClient(mTransportA1.transportName, "caller");
- assertThat(transportClient.getTransportComponent()).isEqualTo(mTransport1.componentName);
+ assertThat(transportClient.getTransportComponent())
+ .isEqualTo(mTransportA1.getTransportComponent());
}
@Test
- public void getTransportClient_forOldNameOfTransportThatChangedName_returnsNull()
+ public void testGetTransportClient_forOldNameOfTransportThatChangedName_returnsNull()
throws Exception {
- TransportManager transportManager =
- createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2);
+ transportManager.registerTransports();
transportManager.updateTransportAttributes(
- mTransport1.componentName, "newName", null, "destinationString", null, null);
+ mTransportA1.getTransportComponent(),
+ "newName",
+ null,
+ "destinationString",
+ null,
+ null);
TransportClient transportClient =
- transportManager.getTransportClient(mTransport1.name, "caller");
+ transportManager.getTransportClient(mTransportA1.transportName, "caller");
assertThat(transportClient).isNull();
}
@Test
- public void getTransportClient_forNewNameOfTransportThatChangedName_returnsCorrectly()
+ public void testGetTransportClient_forNewNameOfTransportThatChangedName_returnsCorrectly()
throws Exception {
- TransportManager transportManager =
- createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2);
+ transportManager.registerTransports();
transportManager.updateTransportAttributes(
- mTransport1.componentName, "newName", null, "destinationString", null, null);
+ mTransportA1.getTransportComponent(),
+ "newName",
+ null,
+ "destinationString",
+ null,
+ null);
- TransportClient transportClient =
- transportManager.getTransportClient("newName", "caller");
+ TransportClient transportClient = transportManager.getTransportClient("newName", "caller");
- assertThat(transportClient.getTransportComponent()).isEqualTo(mTransport1.componentName);
+ assertThat(transportClient.getTransportComponent())
+ .isEqualTo(mTransportA1.getTransportComponent());
}
@Test
- public void getTransportName_forTransportThatChangedName_returnsNewName()
- throws Exception {
- TransportManager transportManager =
- createTransportManagerAndSetUpTransports(
- Arrays.asList(mTransport1, mTransport2), mTransport1.name);
+ public void testGetTransportName_forTransportThatChangedName_returnsNewName() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1, mTransportA2);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2);
+ transportManager.registerTransports();
transportManager.updateTransportAttributes(
- mTransport1.componentName, "newName", null, "destinationString", null, null);
+ mTransportA1.getTransportComponent(),
+ "newName",
+ null,
+ "destinationString",
+ null,
+ null);
- String transportName = transportManager.getTransportName(mTransport1.componentName);
+ String transportName =
+ transportManager.getTransportName(mTransportA1.getTransportComponent());
assertThat(transportName).isEqualTo("newName");
}
@Test
- public void isTransportRegistered_returnsCorrectly() throws Exception {
- TransportManager transportManager =
- createTransportManagerAndSetUpTransports(
- Collections.singletonList(mTransport1),
- Collections.singletonList(mTransport2),
- mTransport1.name);
+ public void testIsTransportRegistered() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1);
+ TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2);
+ transportManager.registerTransports();
- assertThat(transportManager.isTransportRegistered(mTransport1.name)).isTrue();
- assertThat(transportManager.isTransportRegistered(mTransport2.name)).isFalse();
+ boolean isTransportA1Registered =
+ transportManager.isTransportRegistered(mTransportA1.transportName);
+ boolean isTransportA2Registered =
+ transportManager.isTransportRegistered(mTransportA2.transportName);
+
+ assertThat(isTransportA1Registered).isTrue();
+ assertThat(isTransportA2Registered).isFalse();
}
@Test
- public void getTransportAttributes_forRegisteredTransport_returnsCorrectValues()
+ public void testGetTransportAttributes_forRegisteredTransport_returnsCorrectValues()
throws Exception {
- TransportManager transportManager =
- createTransportManagerAndSetUpTransports(
- Collections.singletonList(mTransport1),
- mTransport1.name);
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1);
+ TransportManager transportManager = createTransportManager(mTransportA1);
+ transportManager.registerTransports();
- assertThat(transportManager.getTransportConfigurationIntent(mTransport1.name))
- .isEqualTo(mTransport1.binderInterface.configurationIntent());
- assertThat(transportManager.getTransportDataManagementIntent(mTransport1.name))
- .isEqualTo(mTransport1.binderInterface.dataManagementIntent());
- assertThat(transportManager.getTransportDataManagementLabel(mTransport1.name))
- .isEqualTo(mTransport1.binderInterface.dataManagementLabel());
- assertThat(transportManager.getTransportDirName(mTransport1.name))
- .isEqualTo(mTransport1.binderInterface.transportDirName());
+ Intent configurationIntent =
+ transportManager.getTransportConfigurationIntent(mTransportA1.transportName);
+ Intent dataManagementIntent =
+ transportManager.getTransportDataManagementIntent(mTransportA1.transportName);
+ String dataManagementLabel =
+ transportManager.getTransportDataManagementLabel(mTransportA1.transportName);
+ String transportDirName = transportManager.getTransportDirName(mTransportA1.transportName);
+
+ assertThat(configurationIntent).isEqualTo(mTransportA1.configurationIntent);
+ assertThat(dataManagementIntent).isEqualTo(mTransportA1.dataManagementIntent);
+ assertThat(dataManagementLabel).isEqualTo(mTransportA1.dataManagementLabel);
+ assertThat(transportDirName).isEqualTo(mTransportA1.transportDirName);
}
@Test
- public void getTransportAttributes_forUnregisteredTransport_throws()
- throws Exception {
- TransportManager transportManager =
- createTransportManagerAndSetUpTransports(
- Collections.singletonList(mTransport1),
- Collections.singletonList(mTransport2),
- mTransport1.name);
+ public void testGetTransportAttributes_forUnregisteredTransport_throws() throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1);
+ TransportManager transportManager = createTransportManager(mTransportA1);
+ transportManager.registerTransports();
expectThrows(
TransportNotRegisteredException.class,
- () -> transportManager.getTransportConfigurationIntent(mTransport2.name));
+ () -> transportManager.getTransportConfigurationIntent(mTransportA2.transportName));
expectThrows(
TransportNotRegisteredException.class,
- () -> transportManager.getTransportDataManagementIntent(
- mTransport2.name));
+ () ->
+ transportManager.getTransportDataManagementIntent(
+ mTransportA2.transportName));
expectThrows(
TransportNotRegisteredException.class,
- () -> transportManager.getTransportDataManagementLabel(mTransport2.name));
+ () -> transportManager.getTransportDataManagementLabel(mTransportA2.transportName));
expectThrows(
TransportNotRegisteredException.class,
- () -> transportManager.getTransportDirName(mTransport2.name));
+ () -> transportManager.getTransportDirName(mTransportA2.transportName));
}
- private void setUpPackageWithTransports(String packageName, List<TransportInfo> transports,
- int flags) throws Exception {
+ private List<TransportMock> setUpTransports(TransportData... transports) throws Exception {
+ setUpTransportsForTransportManager(mShadowPackageManager, transports);
+ List<TransportMock> transportMocks = new ArrayList<>(transports.length);
+ for (TransportData transport : transports) {
+ TransportMock transportMock = mockTransport(transport);
+ when(mTransportClientManager.getTransportClient(
+ eq(transport.getTransportComponent()), any()))
+ .thenReturn(transportMock.transportClient);
+ transportMocks.add(transportMock);
+ }
+ return transportMocks;
+ }
+
+ private void setUpPackage(String packageName, int flags) {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = packageName;
packageInfo.applicationInfo = new ApplicationInfo();
packageInfo.applicationInfo.privateFlags = flags;
-
- mPackageManagerShadow.addPackage(packageInfo);
-
- List<ResolveInfo> transportsInfo = new ArrayList<>();
- for (TransportInfo transport : transports) {
- ResolveInfo info = new ResolveInfo();
- info.serviceInfo = new ServiceInfo();
- info.serviceInfo.packageName = packageName;
- info.serviceInfo.name = transport.name;
- transportsInfo.add(info);
- }
-
- Intent intent = new Intent(TransportManager.SERVICE_ACTION_TRANSPORT_HOST);
- intent.setPackage(packageName);
-
- mPackageManagerShadow.addResolveInfoForIntent(intent, transportsInfo);
+ mShadowPackageManager.addPackage(packageInfo);
}
- private TransportManager createTransportManagerAndSetUpTransports(
- List<TransportInfo> availableTransports, String defaultTransportName) throws Exception {
- return createTransportManagerAndSetUpTransports(availableTransports,
- Collections.<TransportInfo>emptyList(), defaultTransportName);
- }
-
- private TransportManager createTransportManagerAndSetUpTransports(
- List<TransportInfo> availableTransports, List<TransportInfo> unavailableTransports,
- String defaultTransportName)
- throws Exception {
- List<String> availableTransportsNames = new ArrayList<>();
- List<ComponentName> availableTransportsComponentNames = new ArrayList<>();
- for (TransportInfo transport : availableTransports) {
- availableTransportsNames.add(transport.name);
- availableTransportsComponentNames.add(transport.componentName);
- }
-
- List<ComponentName> allTransportsComponentNames = new ArrayList<>();
- allTransportsComponentNames.addAll(availableTransportsComponentNames);
- for (TransportInfo transport : unavailableTransports) {
- allTransportsComponentNames.add(transport.componentName);
- }
-
- for (TransportInfo transport : unavailableTransports) {
- ShadowContextImplForBackup.sUnbindableComponents.add(transport.componentName);
- }
-
- setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2),
- ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
-
- TransportManager transportManager = new TransportManager(
- RuntimeEnvironment.application.getApplicationContext(),
- new HashSet<>(allTransportsComponentNames),
- defaultTransportName,
- mTransportBoundListenerStub,
- ShadowLooper.getMainLooper());
- transportManager.onPackageAdded(PACKAGE_NAME);
-
- assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
- availableTransportsComponentNames);
- assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
- availableTransportsNames);
- for (TransportInfo transport : availableTransports) {
- assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.binderInterface))
- .isTrue();
- }
- for (TransportInfo transport : unavailableTransports) {
- assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.binderInterface))
- .isFalse();
- }
-
- mTransportBoundListenerStub.resetState();
-
+ private TransportManager createTransportManager(
+ @Nullable TransportData selectedTransport, TransportData... transports) {
+ Set<ComponentName> whitelist =
+ concat(Stream.of(selectedTransport), Stream.of(transports))
+ .filter(Objects::nonNull)
+ .map(TransportData::getTransportComponent)
+ .collect(toSet());
+ TransportManager transportManager =
+ new TransportManager(
+ mContext,
+ whitelist,
+ selectedTransport != null ? selectedTransport.transportName : null,
+ mTransportClientManager);
+ transportManager.setOnTransportRegisteredListener(mListener);
return transportManager;
}
- private static class TransportInfo {
- public final String packageName;
- public final String name;
- public final ComponentName componentName;
- public final IBackupTransport binderInterface;
- public final IBinder binder;
-
- TransportInfo(
- String packageName,
- String name,
- @Nullable Intent configurationIntent,
- String currentDestinationString,
- @Nullable Intent dataManagementIntent,
- String dataManagementLabel) {
- this.packageName = packageName;
- this.name = name;
- this.componentName = new ComponentName(packageName, name);
- this.binder = mock(IBinder.class);
- IBackupTransport transport = mock(IBackupTransport.class);
- try {
- when(transport.name()).thenReturn(name);
- when(transport.configurationIntent()).thenReturn(configurationIntent);
- when(transport.currentDestinationString()).thenReturn(currentDestinationString);
- when(transport.dataManagementIntent()).thenReturn(dataManagementIntent);
- when(transport.dataManagementLabel()).thenReturn(dataManagementLabel);
- } catch (RemoteException e) {
- // Only here to mock methods that throw RemoteException
- }
- this.binderInterface = transport;
- }
+ private void assertRegisteredTransports(
+ TransportManager transportManager, List<TransportData> transports) {
+ assertThat(transportManager.getRegisteredTransportComponents())
+ .asList()
+ .containsExactlyElementsIn(
+ transports
+ .stream()
+ .map(TransportData::getTransportComponent)
+ .collect(toList()));
+ assertThat(transportManager.getRegisteredTransportNames())
+ .asList()
+ .containsExactlyElementsIn(
+ transports.stream().map(t -> t.transportName).collect(toList()));
}
-
}
diff --git a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java b/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
index dfca901..ace0441 100644
--- a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
@@ -19,8 +19,10 @@
import static android.app.backup.BackupTransport.TRANSPORT_ERROR;
import static android.app.backup.BackupTransport.TRANSPORT_OK;
-import static com.android.server.backup.testing.TransportTestUtils.TRANSPORT_NAME;
-import static com.android.server.backup.testing.TransportTestUtils.TRANSPORT_NAMES;
+import static com.android.server.backup.testing.TransportData.backupTransport;
+import static com.android.server.backup.testing.TransportData.d2dTransport;
+import static com.android.server.backup.testing.TransportData.localTransport;
+import static com.android.server.backup.testing.TransportTestUtils.setUpTransports;
import static com.google.common.truth.Truth.assertThat;
@@ -28,7 +30,6 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -44,7 +45,8 @@
import com.android.server.backup.RefactoredBackupManagerService;
import com.android.server.backup.TransportManager;
import com.android.server.backup.testing.TransportTestUtils;
-import com.android.server.backup.testing.TransportTestUtils.TransportData;
+import com.android.server.backup.testing.TransportData;
+import com.android.server.backup.testing.TransportTestUtils.TransportMock;
import com.android.server.backup.transport.TransportClient;
import com.android.server.testing.FrameworkRobolectricTestRunner;
import com.android.server.testing.SystemLoaderClasses;
@@ -58,7 +60,10 @@
import org.robolectric.annotation.Config;
import java.io.File;
+import java.util.Arrays;
+import java.util.Iterator;
import java.util.List;
+import java.util.stream.Stream;
@RunWith(FrameworkRobolectricTestRunner.class)
@Config(manifest = Config.NONE, sdk = 26)
@@ -68,16 +73,21 @@
@Mock private RefactoredBackupManagerService mBackupManagerService;
@Mock private TransportManager mTransportManager;
@Mock private OnTaskFinishedListener mListener;
- @Mock private IBackupTransport mTransport;
+ @Mock private IBackupTransport mTransportBinder;
@Mock private IBackupObserver mObserver;
@Mock private AlarmManager mAlarmManager;
@Mock private PendingIntent mRunInitIntent;
private File mBaseStateDir;
+ private TransportData mTransport;
+ private String mTransportName;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mTransport = backupTransport();
+ mTransportName = mTransport.transportName;
+
Application context = RuntimeEnvironment.application;
mBaseStateDir = new File(context.getCacheDir(), "base_state_dir");
assertThat(mBaseStateDir.mkdir()).isTrue();
@@ -88,82 +98,76 @@
@Test
public void testRun_callsTransportCorrectly() throws Exception {
- setUpTransport(TRANSPORT_NAME);
- configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_OK);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ setUpTransport(mTransport);
+ configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_OK);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
- verify(mTransport).initializeDevice();
- verify(mTransport).finishBackup();
+ verify(mTransportBinder).initializeDevice();
+ verify(mTransportBinder).finishBackup();
}
@Test
public void testRun_callsBackupManagerCorrectly() throws Exception {
- setUpTransport(TRANSPORT_NAME);
- configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_OK);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ setUpTransport(mTransport);
+ configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_OK);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
verify(mBackupManagerService)
- .recordInitPending(
- false, TRANSPORT_NAME, TransportTestUtils.transportDirName(TRANSPORT_NAME));
+ .recordInitPending(false, mTransportName, mTransport.transportDirName);
verify(mBackupManagerService)
- .resetBackupState(
- eq(
- new File(
- mBaseStateDir,
- TransportTestUtils.transportDirName(TRANSPORT_NAME))));
+ .resetBackupState(eq(new File(mBaseStateDir, mTransport.transportDirName)));
}
@Test
public void testRun_callsObserverAndListenerCorrectly() throws Exception {
- setUpTransport(TRANSPORT_NAME);
- configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_OK);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ setUpTransport(mTransport);
+ configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_OK);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
- verify(mObserver).onResult(eq(TRANSPORT_NAME), eq(TRANSPORT_OK));
+ verify(mObserver).onResult(eq(mTransportName), eq(TRANSPORT_OK));
verify(mObserver).backupFinished(eq(TRANSPORT_OK));
verify(mListener).onFinished(any());
}
@Test
public void testRun_whenInitializeDeviceFails() throws Exception {
- setUpTransport(TRANSPORT_NAME);
- configureTransport(mTransport, TRANSPORT_ERROR, 0);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ setUpTransport(mTransport);
+ configureTransport(mTransportBinder, TRANSPORT_ERROR, 0);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
- verify(mTransport).initializeDevice();
- verify(mTransport, never()).finishBackup();
+ verify(mTransportBinder).initializeDevice();
+ verify(mTransportBinder, never()).finishBackup();
verify(mBackupManagerService)
- .recordInitPending(
- true, TRANSPORT_NAME, TransportTestUtils.transportDirName(TRANSPORT_NAME));
+ .recordInitPending(true, mTransportName, mTransport.transportDirName);
}
@Test
public void testRun_whenInitializeDeviceFails_callsObserverAndListenerCorrectly()
throws Exception {
- setUpTransport(TRANSPORT_NAME);
- configureTransport(mTransport, TRANSPORT_ERROR, 0);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ setUpTransport(mTransport);
+ configureTransport(mTransportBinder, TRANSPORT_ERROR, 0);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
- verify(mObserver).onResult(eq(TRANSPORT_NAME), eq(TRANSPORT_ERROR));
+ verify(mObserver).onResult(eq(mTransportName), eq(TRANSPORT_ERROR));
verify(mObserver).backupFinished(eq(TRANSPORT_ERROR));
verify(mListener).onFinished(any());
}
@Test
public void testRun_whenInitializeDeviceFails_schedulesAlarm() throws Exception {
- setUpTransport(TRANSPORT_NAME);
- configureTransport(mTransport, TRANSPORT_ERROR, 0);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ setUpTransport(mTransport);
+ configureTransport(mTransportBinder, TRANSPORT_ERROR, 0);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
@@ -172,37 +176,36 @@
@Test
public void testRun_whenFinishBackupFails() throws Exception {
- setUpTransport(TRANSPORT_NAME);
- configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_ERROR);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ setUpTransport(mTransport);
+ configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_ERROR);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
- verify(mTransport).initializeDevice();
- verify(mTransport).finishBackup();
+ verify(mTransportBinder).initializeDevice();
+ verify(mTransportBinder).finishBackup();
verify(mBackupManagerService)
- .recordInitPending(
- true, TRANSPORT_NAME, TransportTestUtils.transportDirName(TRANSPORT_NAME));
+ .recordInitPending(true, mTransportName, mTransport.transportDirName);
}
@Test
public void testRun_whenFinishBackupFails_callsObserverAndListenerCorrectly() throws Exception {
- setUpTransport(TRANSPORT_NAME);
- configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_ERROR);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ setUpTransport(mTransport);
+ configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_ERROR);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
- verify(mObserver).onResult(eq(TRANSPORT_NAME), eq(TRANSPORT_ERROR));
+ verify(mObserver).onResult(eq(mTransportName), eq(TRANSPORT_ERROR));
verify(mObserver).backupFinished(eq(TRANSPORT_ERROR));
verify(mListener).onFinished(any());
}
@Test
public void testRun_whenFinishBackupFails_schedulesAlarm() throws Exception {
- setUpTransport(TRANSPORT_NAME);
- configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_ERROR);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ setUpTransport(mTransport);
+ configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_ERROR);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
@@ -211,64 +214,76 @@
@Test
public void testRun_whenOnlyOneTransportFails() throws Exception {
- List<TransportData> transports =
- TransportTestUtils.setUpTransports(
- mTransportManager, TRANSPORT_NAMES[0], TRANSPORT_NAMES[1]);
- configureTransport(transports.get(0).transportMock, TRANSPORT_ERROR, 0);
- configureTransport(transports.get(1).transportMock, TRANSPORT_OK, TRANSPORT_OK);
+ TransportData transport1 = backupTransport();
+ TransportData transport2 = d2dTransport();
+ List<TransportMock> transportMocks =
+ setUpTransports(mTransportManager, transport1, transport2);
+ configureTransport(transportMocks.get(0).transport, TRANSPORT_ERROR, 0);
+ configureTransport(transportMocks.get(1).transport, TRANSPORT_OK, TRANSPORT_OK);
PerformInitializeTask performInitializeTask =
- createPerformInitializeTask(TRANSPORT_NAMES[0], TRANSPORT_NAMES[1]);
+ createPerformInitializeTask(transport1.transportName, transport2.transportName);
performInitializeTask.run();
- verify(transports.get(1).transportMock).initializeDevice();
- verify(mObserver).onResult(eq(TRANSPORT_NAMES[0]), eq(TRANSPORT_ERROR));
- verify(mObserver).onResult(eq(TRANSPORT_NAMES[1]), eq(TRANSPORT_OK));
+ verify(transportMocks.get(1).transport).initializeDevice();
+ verify(mObserver).onResult(eq(transport1.transportName), eq(TRANSPORT_ERROR));
+ verify(mObserver).onResult(eq(transport2.transportName), eq(TRANSPORT_OK));
verify(mObserver).backupFinished(eq(TRANSPORT_ERROR));
}
@Test
public void testRun_withMultipleTransports() throws Exception {
- List<TransportData> transports =
- TransportTestUtils.setUpTransports(mTransportManager, TRANSPORT_NAMES);
- configureTransport(transports.get(0).transportMock, TRANSPORT_OK, TRANSPORT_OK);
- configureTransport(transports.get(1).transportMock, TRANSPORT_OK, TRANSPORT_OK);
- configureTransport(transports.get(2).transportMock, TRANSPORT_OK, TRANSPORT_OK);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAMES);
+ List<TransportMock> transportMocks =
+ setUpTransports(
+ mTransportManager, backupTransport(), d2dTransport(), localTransport());
+ configureTransport(transportMocks.get(0).transport, TRANSPORT_OK, TRANSPORT_OK);
+ configureTransport(transportMocks.get(1).transport, TRANSPORT_OK, TRANSPORT_OK);
+ configureTransport(transportMocks.get(2).transport, TRANSPORT_OK, TRANSPORT_OK);
+ String[] transportNames =
+ Stream.of(new TransportData[] {backupTransport(), d2dTransport(), localTransport()})
+ .map(t -> t.transportName)
+ .toArray(String[]::new);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(transportNames);
performInitializeTask.run();
- for (TransportData transport : transports) {
+ Iterator<TransportData> transportsIterator =
+ Arrays.asList(
+ new TransportData[] {
+ backupTransport(), d2dTransport(), localTransport()
+ })
+ .iterator();
+ for (TransportMock transportMock : transportMocks) {
+ TransportData transport = transportsIterator.next();
verify(mTransportManager).getTransportClient(eq(transport.transportName), any());
verify(mTransportManager)
- .disposeOfTransportClient(eq(transport.transportClientMock), any());
+ .disposeOfTransportClient(eq(transportMock.transportClient), any());
}
}
@Test
public void testRun_whenOnlyOneTransportFails_disposesAllTransports() throws Exception {
- List<TransportData> transports =
- TransportTestUtils.setUpTransports(
- mTransportManager, TRANSPORT_NAMES[0], TRANSPORT_NAMES[1]);
- configureTransport(transports.get(0).transportMock, TRANSPORT_ERROR, 0);
- configureTransport(transports.get(1).transportMock, TRANSPORT_OK, TRANSPORT_OK);
+ TransportData transport1 = backupTransport();
+ TransportData transport2 = d2dTransport();
+ List<TransportMock> transportMocks =
+ setUpTransports(mTransportManager, transport1, transport2);
+ configureTransport(transportMocks.get(0).transport, TRANSPORT_ERROR, 0);
+ configureTransport(transportMocks.get(1).transport, TRANSPORT_OK, TRANSPORT_OK);
PerformInitializeTask performInitializeTask =
- createPerformInitializeTask(TRANSPORT_NAMES[0], TRANSPORT_NAMES[1]);
+ createPerformInitializeTask(transport1.transportName, transport2.transportName);
performInitializeTask.run();
verify(mTransportManager)
- .disposeOfTransportClient(eq(transports.get(0).transportClientMock), any());
+ .disposeOfTransportClient(eq(transportMocks.get(0).transportClient), any());
verify(mTransportManager)
- .disposeOfTransportClient(eq(transports.get(1).transportClientMock), any());
+ .disposeOfTransportClient(eq(transportMocks.get(1).transportClient), any());
}
@Test
public void testRun_whenTransportNotRegistered() throws Exception {
- TransportTestUtils.setUpTransports(
- mTransportManager, new TransportData(TRANSPORT_NAME, null, null));
-
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ setUpTransports(mTransportManager, mTransport.unregistered());
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
@@ -279,16 +294,15 @@
@Test
public void testRun_whenOnlyOneTransportNotRegistered() throws Exception {
- List<TransportData> transports =
- TransportTestUtils.setUpTransports(
- mTransportManager,
- new TransportData(TRANSPORT_NAMES[0], null, null),
- new TransportData(TRANSPORT_NAMES[1]));
- String registeredTransportName = transports.get(1).transportName;
- IBackupTransport registeredTransport = transports.get(1).transportMock;
- TransportClient registeredTransportClient = transports.get(1).transportClientMock;
+ TransportData transport1 = backupTransport().unregistered();
+ TransportData transport2 = d2dTransport();
+ List<TransportMock> transportMocks =
+ setUpTransports(mTransportManager, transport1, transport2);
+ String registeredTransportName = transport2.transportName;
+ IBackupTransport registeredTransport = transportMocks.get(1).transport;
+ TransportClient registeredTransportClient = transportMocks.get(1).transportClient;
PerformInitializeTask performInitializeTask =
- createPerformInitializeTask(TRANSPORT_NAMES[0], TRANSPORT_NAMES[1]);
+ createPerformInitializeTask(transport1.transportName, transport2.transportName);
performInitializeTask.run();
@@ -299,25 +313,24 @@
@Test
public void testRun_whenTransportNotAvailable() throws Exception {
- TransportClient transportClient = mock(TransportClient.class);
- TransportTestUtils.setUpTransports(
- mTransportManager, new TransportData(TRANSPORT_NAME, null, transportClient));
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ TransportMock transportMock = setUpTransport(mTransport.unavailable());
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
- verify(mTransportManager).disposeOfTransportClient(eq(transportClient), any());
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(transportMock.transportClient), any());
verify(mObserver).backupFinished(eq(TRANSPORT_ERROR));
verify(mListener).onFinished(any());
}
@Test
public void testRun_whenTransportThrowsDeadObjectException() throws Exception {
- TransportClient transportClient = mock(TransportClient.class);
- TransportTestUtils.setUpTransports(
- mTransportManager, new TransportData(TRANSPORT_NAME, mTransport, transportClient));
- when(mTransport.initializeDevice()).thenThrow(DeadObjectException.class);
- PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME);
+ TransportMock transportMock = setUpTransport(mTransport);
+ IBackupTransport transport = transportMock.transport;
+ TransportClient transportClient = transportMock.transportClient;
+ when(transport.initializeDevice()).thenThrow(DeadObjectException.class);
+ PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName);
performInitializeTask.run();
@@ -343,9 +356,10 @@
when(transportMock.finishBackup()).thenReturn(finishBackupStatus);
}
- private void setUpTransport(String transportName) throws Exception {
- TransportTestUtils.setUpTransport(
- mTransportManager,
- new TransportData(transportName, mTransport, mock(TransportClient.class)));
+ private TransportMock setUpTransport(TransportData transport) throws Exception {
+ TransportMock transportMock =
+ TransportTestUtils.setUpTransport(mTransportManager, transport);
+ mTransportBinder = transportMock.transport;
+ return transportMock;
}
}
diff --git a/services/robotests/src/com/android/server/backup/testing/ShadowBackupPolicyEnforcer.java b/services/robotests/src/com/android/server/backup/testing/ShadowBackupPolicyEnforcer.java
new file mode 100644
index 0000000..88b30da
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/testing/ShadowBackupPolicyEnforcer.java
@@ -0,0 +1,24 @@
+package com.android.server.backup.testing;
+
+import android.content.ComponentName;
+
+import com.android.server.backup.BackupPolicyEnforcer;
+import com.android.server.backup.RefactoredBackupManagerService;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+@Implements(BackupPolicyEnforcer.class)
+public class ShadowBackupPolicyEnforcer {
+
+ private static ComponentName sMandatoryBackupTransport;
+
+ public static void setMandatoryBackupTransport(ComponentName backupTransportComponent) {
+ sMandatoryBackupTransport = backupTransportComponent;
+ }
+
+ @Implementation
+ public ComponentName getMandatoryBackupTransport() {
+ return sMandatoryBackupTransport;
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/testing/TestUtils.java b/services/robotests/src/com/android/server/backup/testing/TestUtils.java
new file mode 100644
index 0000000..1be298d
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/testing/TestUtils.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.testing;
+
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
+
+import java.util.concurrent.Callable;
+
+public class TestUtils {
+ /**
+ * Calls {@link Runnable#run()} and returns if no exception is thrown. Otherwise, if the
+ * exception is unchecked, rethrow it; if it's checked wrap in a {@link RuntimeException} and
+ * throw.
+ *
+ * <p><b>Warning:</b>DON'T use this outside tests. A wrapped checked exception is just a failure
+ * in a test.
+ */
+ public static void uncheck(ThrowingRunnable runnable) {
+ try {
+ runnable.runOrThrow();
+ } catch (Exception e) {
+ throw wrapIfChecked(e);
+ }
+ }
+
+ /**
+ * Calls {@link Callable#call()} and returns the value if no exception is thrown. Otherwise, if
+ * the exception is unchecked, rethrow it; if it's checked wrap in a {@link RuntimeException}
+ * and throw.
+ *
+ * <p><b>Warning:</b>DON'T use this outside tests. A wrapped checked exception is just a failure
+ * in a test.
+ */
+ public static <T> T uncheck(Callable<T> callable) {
+ try {
+ return callable.call();
+ } catch (Exception e) {
+ throw wrapIfChecked(e);
+ }
+ }
+
+ /**
+ * Wrap {@code e} in a {@link RuntimeException} only if it's not one already, in which case it's
+ * returned.
+ */
+ public static RuntimeException wrapIfChecked(Exception e) {
+ if (e instanceof RuntimeException) {
+ return (RuntimeException) e;
+ }
+ return new RuntimeException(e);
+ }
+
+ private TestUtils() {}
+}
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportBoundListenerStub.java b/services/robotests/src/com/android/server/backup/testing/TransportBoundListenerStub.java
deleted file mode 100644
index 84ac2c2..0000000
--- a/services/robotests/src/com/android/server/backup/testing/TransportBoundListenerStub.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2017 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.backup.testing;
-
-import com.android.internal.backup.IBackupTransport;
-import com.android.server.backup.TransportManager;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Stub implementation of TransportBoundListener, which returns given result and can tell whether
- * it was called for given transport.
- */
-public class TransportBoundListenerStub implements
- TransportManager.TransportBoundListener {
- private boolean mAlwaysReturnSuccess;
- private Set<IBackupTransport> mTransportsCalledFor = new HashSet<>();
-
- public TransportBoundListenerStub(boolean alwaysReturnSuccess) {
- this.mAlwaysReturnSuccess = alwaysReturnSuccess;
- }
-
- @Override
- public boolean onTransportBound(IBackupTransport binder) {
- mTransportsCalledFor.add(binder);
- return mAlwaysReturnSuccess;
- }
-
- /**
- * Returns whether the listener was called for the specified transport at least once.
- */
- public boolean isCalledForTransport(IBackupTransport binder) {
- return mTransportsCalledFor.contains(binder);
- }
-
- /**
- * Returns whether the listener was called at least once.
- */
- public boolean isCalled() {
- return !mTransportsCalledFor.isEmpty();
- }
-
- /**
- * Resets listener calls.
- */
- public void resetState() {
- mTransportsCalledFor.clear();
- }
-}
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportData.java b/services/robotests/src/com/android/server/backup/testing/TransportData.java
new file mode 100644
index 0000000..9feaa8e
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/testing/TransportData.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.testing;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Intent;
+
+public class TransportData {
+ // No constants since new Intent() can't be called in static context because of Robolectric
+ public static TransportData backupTransport() {
+ return new TransportData(
+ "com.google.android.gms/.backup.BackupTransportService",
+ "com.google.android.gms/.backup.BackupTransportService",
+ "com.google.android.gms.backup.BackupTransportService",
+ new Intent(),
+ "user@gmail.com",
+ new Intent(),
+ "Google Account");
+ }
+
+ public static TransportData d2dTransport() {
+ return new TransportData(
+ "com.google.android.gms/.backup.migrate.service.D2dTransport",
+ "com.google.android.gms/.backup.component.D2dTransportService",
+ "d2dMigrateTransport",
+ null,
+ "Moving data to new device",
+ null,
+ "");
+ }
+
+ public static TransportData localTransport() {
+ return new TransportData(
+ "android/com.android.internal.backup.LocalTransport",
+ "android/com.android.internal.backup.LocalTransportService",
+ "com.android.internal.backup.LocalTransport",
+ null,
+ "Backing up to debug-only private cache",
+ null,
+ "");
+ }
+
+ public static TransportData genericTransport(String packageName, String className) {
+ return new TransportData(
+ packageName + "/." + className,
+ packageName + "/." + className + "Service",
+ packageName + "." + className,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ "dataManagementLabel");
+ }
+
+ @TransportTestUtils.TransportStatus
+ public int transportStatus;
+ public final String transportName;
+ private final String transportComponentShort;
+ @Nullable
+ public String transportDirName;
+ @Nullable public Intent configurationIntent;
+ @Nullable public String currentDestinationString;
+ @Nullable public Intent dataManagementIntent;
+ @Nullable public String dataManagementLabel;
+
+ private TransportData(
+ @TransportTestUtils.TransportStatus int transportStatus,
+ String transportName,
+ String transportComponentShort,
+ String transportDirName,
+ Intent configurationIntent,
+ String currentDestinationString,
+ Intent dataManagementIntent,
+ String dataManagementLabel) {
+ this.transportStatus = transportStatus;
+ this.transportName = transportName;
+ this.transportComponentShort = transportComponentShort;
+ this.transportDirName = transportDirName;
+ this.configurationIntent = configurationIntent;
+ this.currentDestinationString = currentDestinationString;
+ this.dataManagementIntent = dataManagementIntent;
+ this.dataManagementLabel = dataManagementLabel;
+ }
+
+ public TransportData(
+ String transportName,
+ String transportComponentShort,
+ String transportDirName,
+ Intent configurationIntent,
+ String currentDestinationString,
+ Intent dataManagementIntent,
+ String dataManagementLabel) {
+ this(
+ TransportTestUtils.TransportStatus.REGISTERED_AVAILABLE,
+ transportName,
+ transportComponentShort,
+ transportDirName,
+ configurationIntent,
+ currentDestinationString,
+ dataManagementIntent,
+ dataManagementLabel);
+ }
+
+ /**
+ * Not field because otherwise we'd have to call ComponentName::new in static context and
+ * Robolectric does not like this.
+ */
+ public ComponentName getTransportComponent() {
+ return ComponentName.unflattenFromString(transportComponentShort);
+ }
+
+ public TransportData unavailable() {
+ return new TransportData(
+ TransportTestUtils.TransportStatus.REGISTERED_UNAVAILABLE,
+ transportName,
+ transportComponentShort,
+ transportDirName,
+ configurationIntent,
+ currentDestinationString,
+ dataManagementIntent,
+ dataManagementLabel);
+ }
+
+ public TransportData unregistered() {
+ return new TransportData(
+ TransportTestUtils.TransportStatus.UNREGISTERED,
+ transportName,
+ transportComponentShort,
+ transportDirName,
+ configurationIntent,
+ currentDestinationString,
+ dataManagementIntent,
+ dataManagementLabel);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java b/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
index 9770e40..e1dc7b5e 100644
--- a/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
@@ -16,13 +16,23 @@
package com.android.server.backup.testing;
+import static com.android.server.backup.testing.TestUtils.uncheck;
+
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static java.util.stream.Collectors.toList;
+
import android.annotation.Nullable;
import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.RemoteException;
+import android.support.annotation.IntDef;
import com.android.internal.backup.IBackupTransport;
import com.android.server.backup.TransportManager;
@@ -30,85 +40,82 @@
import com.android.server.backup.transport.TransportNotAvailableException;
import com.android.server.backup.transport.TransportNotRegisteredException;
-import java.util.Arrays;
+import org.robolectric.shadows.ShadowPackageManager;
+
import java.util.List;
+import java.util.stream.Stream;
public class TransportTestUtils {
- public static final String[] TRANSPORT_NAMES = {
- "android/com.android.internal.backup.LocalTransport",
- "com.google.android.gms/.backup.migrate.service.D2dTransport",
- "com.google.android.gms/.backup.BackupTransportService"
- };
+ /**
+ * Differently from {@link #setUpTransports(TransportManager, TransportData...)}, which
+ * configures {@link TransportManager}, this is meant to mock the environment for a real
+ * TransportManager.
+ */
+ public static void setUpTransportsForTransportManager(
+ ShadowPackageManager shadowPackageManager, TransportData... transports)
+ throws Exception {
+ for (TransportData transport : transports) {
+ ComponentName transportComponent = transport.getTransportComponent();
+ String packageName = transportComponent.getPackageName();
+ ResolveInfo resolveInfo = resolveInfo(transportComponent);
+ shadowPackageManager.addResolveInfoForIntent(transportIntent(), resolveInfo);
+ shadowPackageManager.addResolveInfoForIntent(
+ transportIntent().setPackage(packageName), resolveInfo);
+ }
+ }
- public static final String TRANSPORT_NAME = TRANSPORT_NAMES[0];
+ private static Intent transportIntent() {
+ return new Intent(TransportManager.SERVICE_ACTION_TRANSPORT_HOST);
+ }
- /** {@code transportName} has to be in the {@link ComponentName} format (with '/') */
- public static TransportData setUpCurrentTransport(
- TransportManager transportManager, String transportName) throws Exception {
- TransportData transport = setUpTransports(transportManager, transportName).get(0);
- when(transportManager.getCurrentTransportClient(any()))
- .thenReturn(transport.transportClientMock);
- return transport;
+ private static ResolveInfo resolveInfo(ComponentName transportComponent) {
+ ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.serviceInfo = new ServiceInfo();
+ resolveInfo.serviceInfo.packageName = transportComponent.getPackageName();
+ resolveInfo.serviceInfo.name = transportComponent.getClassName();
+ return resolveInfo;
}
/** {@code transportName} has to be in the {@link ComponentName} format (with '/') */
- public static List<TransportData> setUpTransports(
- TransportManager transportManager, String... transportNames) throws Exception {
- return setUpTransports(
- transportManager,
- Arrays.stream(transportNames)
- .map(TransportData::new)
- .toArray(TransportData[]::new));
+ public static TransportMock setUpCurrentTransport(
+ TransportManager transportManager, TransportData transport) throws Exception {
+ TransportMock transportMock = setUpTransports(transportManager, transport).get(0);
+ if (transportMock.transportClient != null) {
+ when(transportManager.getCurrentTransportClient(any()))
+ .thenReturn(transportMock.transportClient);
+ }
+ return transportMock;
}
/** @see #setUpTransport(TransportManager, TransportData) */
- public static List<TransportData> setUpTransports(
+ public static List<TransportMock> setUpTransports(
TransportManager transportManager, TransportData... transports) throws Exception {
- for (TransportData transport : transports) {
- setUpTransport(transportManager, transport);
- }
- return Arrays.asList(transports);
+ return Stream.of(transports)
+ .map(transport -> uncheck(() -> setUpTransport(transportManager, transport)))
+ .collect(toList());
}
- /**
- * Configures transport according to {@link TransportData}:
- *
- * <ul>
- * <li>{@link TransportData#transportMock} {@code null} means transport not available.
- * <li>{@link TransportData#transportClientMock} {@code null} means transport not registered.
- * </ul>
- */
- public static void setUpTransport(TransportManager transportManager, TransportData transport)
- throws Exception {
+ public static TransportMock setUpTransport(
+ TransportManager transportManager, TransportData transport) throws Exception {
+ int status = transport.transportStatus;
String transportName = transport.transportName;
- String transportDirName = transportDirName(transportName);
- ComponentName transportComponent = transportComponentName(transportName);
- IBackupTransport transportMock = transport.transportMock;
- TransportClient transportClientMock = transport.transportClientMock;
+ ComponentName transportComponent = transport.getTransportComponent();
+ String transportDirName = transport.transportDirName;
- if (transportClientMock != null) {
+ TransportMock transportMock = mockTransport(transport);
+ if (status == TransportStatus.REGISTERED_AVAILABLE
+ || status == TransportStatus.REGISTERED_UNAVAILABLE) {
// Transport registered
when(transportManager.getTransportClient(eq(transportName), any()))
- .thenReturn(transportClientMock);
+ .thenReturn(transportMock.transportClient);
when(transportManager.getTransportClientOrThrow(eq(transportName), any()))
- .thenReturn(transportClientMock);
+ .thenReturn(transportMock.transportClient);
when(transportManager.getTransportName(transportComponent)).thenReturn(transportName);
when(transportManager.getTransportDirName(eq(transportName)))
.thenReturn(transportDirName);
when(transportManager.getTransportDirName(eq(transportComponent)))
.thenReturn(transportDirName);
- when(transportClientMock.getTransportComponent()).thenReturn(transportComponent);
-
- if (transportMock != null) {
- // Transport registered and available
- when(transportClientMock.connectOrThrow(any())).thenReturn(transportMock);
- when(transportMock.name()).thenReturn(transportName);
- when(transportMock.transportDirName()).thenReturn(transportDirName);
- } else {
- // Transport registered but unavailable
- when(transportClientMock.connectOrThrow(any()))
- .thenThrow(TransportNotAvailableException.class);
- }
+ // TODO: Mock rest of description methods
} else {
// Transport not registered
when(transportManager.getTransportClient(eq(transportName), any())).thenReturn(null);
@@ -121,34 +128,73 @@
when(transportManager.getTransportDirName(eq(transportComponent)))
.thenThrow(TransportNotRegisteredException.class);
}
+ return transportMock;
}
- /** {@code transportName} has to be in the {@link ComponentName} format (with '/') */
- public static ComponentName transportComponentName(String transportName) {
- return ComponentName.unflattenFromString(transportName);
- }
+ public static TransportMock mockTransport(TransportData transport) throws Exception {
+ final TransportClient transportClientMock;
+ int status = transport.transportStatus;
+ ComponentName transportComponent = transport.getTransportComponent();
+ if (status == TransportStatus.REGISTERED_AVAILABLE
+ || status == TransportStatus.REGISTERED_UNAVAILABLE) {
+ // Transport registered
+ transportClientMock = mock(TransportClient.class);
+ when(transportClientMock.getTransportComponent()).thenReturn(transportComponent);
+ if (status == TransportStatus.REGISTERED_AVAILABLE) {
+ // Transport registered and available
+ IBackupTransport transportMock = mockTransportBinder(transport);
+ when(transportClientMock.connectOrThrow(any())).thenReturn(transportMock);
- public static String transportDirName(String transportName) {
- return transportName + "_dir_name";
- }
+ return new TransportMock(transportClientMock, transportMock);
+ } else {
+ // Transport registered but unavailable
+ when(transportClientMock.connectOrThrow(any()))
+ .thenThrow(TransportNotAvailableException.class);
- public static class TransportData {
- public final String transportName;
- @Nullable public final IBackupTransport transportMock;
- @Nullable public final TransportClient transportClientMock;
-
- public TransportData(
- String transportName,
- @Nullable IBackupTransport transportMock,
- @Nullable TransportClient transportClientMock) {
- this.transportName = transportName;
- this.transportMock = transportMock;
- this.transportClientMock = transportClientMock;
+ return new TransportMock(transportClientMock, null);
+ }
+ } else {
+ // Transport not registered
+ return new TransportMock(null, null);
}
+ }
- public TransportData(String transportName) {
- this(transportName, mock(IBackupTransport.class), mock(TransportClient.class));
+ private static IBackupTransport mockTransportBinder(TransportData transport) throws Exception {
+ IBackupTransport transportBinder = mock(IBackupTransport.class);
+ try {
+ when(transportBinder.name()).thenReturn(transport.transportName);
+ when(transportBinder.transportDirName()).thenReturn(transport.transportDirName);
+ when(transportBinder.configurationIntent()).thenReturn(transport.configurationIntent);
+ when(transportBinder.currentDestinationString())
+ .thenReturn(transport.currentDestinationString);
+ when(transportBinder.dataManagementIntent()).thenReturn(transport.dataManagementIntent);
+ when(transportBinder.dataManagementLabel()).thenReturn(transport.dataManagementLabel);
+ } catch (RemoteException e) {
+ fail("RemoteException?");
}
+ return transportBinder;
+ }
+
+ public static class TransportMock {
+ @Nullable public final TransportClient transportClient;
+ @Nullable public final IBackupTransport transport;
+
+ private TransportMock(
+ @Nullable TransportClient transportClient, @Nullable IBackupTransport transport) {
+ this.transportClient = transportClient;
+ this.transport = transport;
+ }
+ }
+
+ @IntDef({
+ TransportStatus.REGISTERED_AVAILABLE,
+ TransportStatus.REGISTERED_UNAVAILABLE,
+ TransportStatus.UNREGISTERED
+ })
+ public @interface TransportStatus {
+ int REGISTERED_AVAILABLE = 0;
+ int REGISTERED_UNAVAILABLE = 1;
+ int UNREGISTERED = 2;
}
private TransportTestUtils() {}
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java b/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
index 3bc0b30..10442b7 100644
--- a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
+++ b/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
@@ -17,9 +17,7 @@
package com.android.server.backup.transport;
import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST;
-
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -36,12 +34,12 @@
import android.os.Looper;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
-
import com.android.internal.backup.IBackupTransport;
+import com.android.server.EventLogTags;
import com.android.server.backup.TransportManager;
import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.ShadowEventLog;
import com.android.server.testing.SystemLoaderClasses;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,7 +50,7 @@
import org.robolectric.shadows.ShadowLooper;
@RunWith(FrameworkRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, sdk = 26)
+@Config(manifest = Config.NONE, sdk = 26, shadows = {ShadowEventLog.class})
@SystemLoaderClasses({TransportManager.class, TransportClient.class})
@Presubmit
public class TransportClientTest {
@@ -64,6 +62,7 @@
@Mock private IBackupTransport.Stub mIBackupTransport;
private TransportClient mTransportClient;
private ComponentName mTransportComponent;
+ private String mTransportString;
private Intent mBindIntent;
private ShadowLooper mShadowLooper;
@@ -75,14 +74,11 @@
mShadowLooper = shadowOf(mainLooper);
mTransportComponent =
new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".transport.Transport");
+ mTransportString = mTransportComponent.flattenToShortString();
mBindIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(mTransportComponent);
mTransportClient =
new TransportClient(
- mContext,
- mBindIntent,
- mTransportComponent,
- "1",
- new Handler(mainLooper));
+ mContext, mBindIntent, mTransportComponent, "1", new Handler(mainLooper));
when(mContext.bindServiceAsUser(
eq(mBindIntent),
@@ -169,7 +165,7 @@
}
@Test
- public void testConnectAsync_whenFrameworkDoesntBind_releasesConnection() throws Exception {
+ public void testConnectAsync_whenFrameworkDoesNotBind_releasesConnection() throws Exception {
when(mContext.bindServiceAsUser(
eq(mBindIntent),
any(ServiceConnection.class),
@@ -213,32 +209,110 @@
.onTransportConnectionResult(isNull(), eq(mTransportClient));
}
- // TODO(b/69153972): Support SDK 26 API (ServiceConnection.inBindingDied) for transport tests
- /*@Test
+ @Test
public void testConnectAsync_callsListenerIfBindingDies() throws Exception {
- mTransportClient.connectAsync(mTransportListener, "caller");
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
connection.onBindingDied(mTransportComponent);
mShadowLooper.runToEndOfTasks();
- verify(mTransportListener).onTransportBound(isNull(), eq(mTransportClient));
+ verify(mTransportConnectionListener)
+ .onTransportConnectionResult(isNull(), eq(mTransportClient));
}
@Test
public void testConnectAsync_whenPendingConnection_callsListenersIfBindingDies()
throws Exception {
- mTransportClient.connectAsync(mTransportListener, "caller1");
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
- mTransportClient.connectAsync(mTransportListener2, "caller2");
+ mTransportClient.connectAsync(mTransportConnectionListener2, "caller2");
connection.onBindingDied(mTransportComponent);
mShadowLooper.runToEndOfTasks();
- verify(mTransportListener).onTransportBound(isNull(), eq(mTransportClient));
- verify(mTransportListener2).onTransportBound(isNull(), eq(mTransportClient));
- }*/
+ verify(mTransportConnectionListener)
+ .onTransportConnectionResult(isNull(), eq(mTransportClient));
+ verify(mTransportConnectionListener2)
+ .onTransportConnectionResult(isNull(), eq(mTransportClient));
+ }
+
+ @Test
+ public void testConnectAsync_beforeFrameworkCall_logsBoundTransition() {
+ ShadowEventLog.clearEvents();
+
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 1);
+ }
+
+ @Test
+ public void testConnectAsync_afterOnServiceConnected_logsBoundAndConnectedTransitions() {
+ ShadowEventLog.clearEvents();
+
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+ connection.onServiceConnected(mTransportComponent, mIBackupTransport);
+
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 1);
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_CONNECTION, mTransportString, 1);
+ }
+
+ @Test
+ public void testConnectAsync_afterOnBindingDied_logsBoundAndUnboundTransitions() {
+ ShadowEventLog.clearEvents();
+
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+ connection.onBindingDied(mTransportComponent);
+
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 1);
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 0);
+ }
+
+ @Test
+ public void testUnbind_whenConnected_logsDisconnectedAndUnboundTransitions() {
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+ connection.onServiceConnected(mTransportComponent, mIBackupTransport);
+ ShadowEventLog.clearEvents();
+
+ mTransportClient.unbind("caller1");
+
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_CONNECTION, mTransportString, 0);
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 0);
+ }
+
+ @Test
+ public void testOnServiceDisconnected_whenConnected_logsDisconnectedAndUnboundTransitions() {
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+ connection.onServiceConnected(mTransportComponent, mIBackupTransport);
+ ShadowEventLog.clearEvents();
+
+ connection.onServiceDisconnected(mTransportComponent);
+
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_CONNECTION, mTransportString, 0);
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 0);
+ }
+
+ @Test
+ public void testOnBindingDied_whenConnected_logsDisconnectedAndUnboundTransitions() {
+ mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
+ ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
+ connection.onServiceConnected(mTransportComponent, mIBackupTransport);
+ ShadowEventLog.clearEvents();
+
+ connection.onBindingDied(mTransportComponent);
+
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_CONNECTION, mTransportString, 0);
+ assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 0);
+ }
+
+ private void assertEventLogged(int tag, Object... values) {
+ assertThat(ShadowEventLog.hasEvent(tag, values)).isTrue();
+ }
private ServiceConnection verifyBindServiceAsUserAndCaptureServiceConnection(Context context) {
ArgumentCaptor<ServiceConnection> connectionCaptor =
diff --git a/services/robotests/src/com/android/server/testing/ShadowEventLog.java b/services/robotests/src/com/android/server/testing/ShadowEventLog.java
new file mode 100644
index 0000000..b8059f4
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/ShadowEventLog.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.testing;
+
+import android.util.EventLog;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+@Implements(EventLog.class)
+public class ShadowEventLog {
+ private final static LinkedHashSet<Entry> ENTRIES = new LinkedHashSet<>();
+
+ @Implementation
+ public static int writeEvent(int tag, Object... values) {
+ ENTRIES.add(new Entry(tag, Arrays.asList(values)));
+ // Currently we don't care about the return value, if we do, estimate it correctly
+ return 0;
+ }
+
+ public static boolean hasEvent(int tag, Object... values) {
+ return ENTRIES.contains(new Entry(tag, Arrays.asList(values)));
+ }
+
+ public static void clearEvents() {
+ ENTRIES.clear();
+ }
+
+ public static class Entry {
+ public final int tag;
+ public final List<Object> values;
+
+ public Entry(int tag, List<Object> values) {
+ this.tag = tag;
+ this.values = values;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Entry entry = (Entry) o;
+ return tag == entry.tag && values.equals(entry.values);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = tag;
+ result = 31 * result + values.hashCode();
+ return result;
+ }
+ }
+}
diff --git a/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowContextImpl.java b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowContextImpl.java
new file mode 100644
index 0000000..6d22073
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowContextImpl.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.testing.shadows;
+
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.UserHandle;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowContextImpl;
+
+@Implements(className = ShadowContextImpl.CLASS_NAME, inheritImplementationMethods = true)
+public class FrameworkShadowContextImpl extends ShadowContextImpl {
+ @Implementation
+ public boolean bindServiceAsUser(
+ Intent service,
+ ServiceConnection connection,
+ int flags,
+ UserHandle user) {
+ return bindService(service, connection, flags);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/testing/ShadowPackageManagerForBackup.java b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowPackageManager.java
similarity index 83%
rename from services/robotests/src/com/android/server/backup/testing/ShadowPackageManagerForBackup.java
rename to services/robotests/src/com/android/server/testing/shadows/FrameworkShadowPackageManager.java
index b64b59d..5cdbe7f 100644
--- a/services/robotests/src/com/android/server/backup/testing/ShadowPackageManagerForBackup.java
+++ b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowPackageManager.java
@@ -14,22 +14,18 @@
* limitations under the License
*/
-package com.android.server.backup.testing;
+package com.android.server.testing.shadows;
import android.app.ApplicationPackageManager;
import android.content.Intent;
import android.content.pm.ResolveInfo;
-
+import java.util.List;
import org.robolectric.annotation.Implements;
import org.robolectric.shadows.ShadowApplicationPackageManager;
-import java.util.List;
-
-/**
- * Implementation of PackageManager for Robolectric which handles queryIntentServicesAsUser().
- */
+/** Extension of ShadowApplicationPackageManager */
@Implements(value = ApplicationPackageManager.class, inheritImplementationMethods = true)
-public class ShadowPackageManagerForBackup extends ShadowApplicationPackageManager {
+public class FrameworkShadowPackageManager extends ShadowApplicationPackageManager {
@Override
public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
return queryIntentServices(intent, flags);
diff --git a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
index 66d0da1..6a21931 100644
--- a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
@@ -42,6 +42,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.BatteryManager;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager.ServiceType;
@@ -50,13 +51,17 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Settings.Global;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.test.mock.MockContentResolver;
import android.util.ArraySet;
import android.util.Pair;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.ForceAppStandbyTracker.Listener;
import org.junit.Before;
@@ -102,6 +107,9 @@
PowerManagerInternal injectPowerManagerInternal() {
return mMockPowerManagerInternal;
}
+
+ @Override
+ boolean isSmallBatteryDevice() { return mIsSmallBatteryDevice; };
}
private static final int UID_1 = Process.FIRST_APPLICATION_UID + 1;
@@ -137,7 +145,11 @@
private Consumer<PowerSaveState> mPowerSaveObserver;
private BroadcastReceiver mReceiver;
+ private MockContentResolver mMockContentResolver;
+ private FakeSettingsProvider mFakeSettingsProvider;
+
private boolean mPowerSaveMode;
+ private boolean mIsSmallBatteryDevice;
private final ArraySet<Pair<Integer, String>> mRestrictedPackages = new ArraySet();
@@ -174,13 +186,17 @@
}
private void callStart(ForceAppStandbyTrackerTestable instance) throws RemoteException {
-
// Set up functions that start() calls.
when(mMockPowerManagerInternal.getLowPowerState(eq(ServiceType.FORCE_ALL_APPS_STANDBY)))
.thenAnswer(inv -> getPowerSaveState());
when(mMockAppOpsManager.getPackagesForOps(
any(int[].class)
- )).thenAnswer(inv -> new ArrayList<AppOpsManager.PackageOps>());
+ )).thenAnswer(inv -> new ArrayList<AppOpsManager.PackageOps>());
+
+ mMockContentResolver = new MockContentResolver();
+ mFakeSettingsProvider = new FakeSettingsProvider();
+ when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
+ mMockContentResolver.addProvider(Settings.AUTHORITY, mFakeSettingsProvider);
// Call start.
instance.start();
@@ -208,7 +224,6 @@
verify(mMockPowerManagerInternal).registerLowPowerModeObserver(
eq(ServiceType.FORCE_ALL_APPS_STANDBY),
powerSaveObserverCaptor.capture());
-
verify(mMockContext).registerReceiver(
receiverCaptor.capture(), any(IntentFilter.class));
@@ -221,6 +236,7 @@
assertNotNull(mAppOpsCallback);
assertNotNull(mPowerSaveObserver);
assertNotNull(mReceiver);
+ assertNotNull(instance.mFlagsObserver);
}
private void setAppOps(int uid, String packageName, boolean restrict) throws RemoteException {
@@ -822,6 +838,33 @@
assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2));
}
+ @Test
+ public void testSmallBatteryAndCharging() throws Exception {
+ // This is a small battery device
+ mIsSmallBatteryDevice = true;
+
+ final ForceAppStandbyTrackerTestable instance = newInstance();
+ callStart(instance);
+ assertFalse(instance.isForceAllAppsStandbyEnabled());
+
+ // Setting/experiment for all app standby for small battery is enabled
+ Global.putInt(mMockContentResolver, Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 1);
+ instance.mFlagsObserver.onChange(true,
+ Global.getUriFor(Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED));
+ assertTrue(instance.isForceAllAppsStandbyEnabled());
+
+ // When battery is charging, force app standby is disabled
+ Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING);
+ mReceiver.onReceive(mMockContext, intent);
+ assertFalse(instance.isForceAllAppsStandbyEnabled());
+
+ // When battery stops charging, force app standby is enabled
+ intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_DISCHARGING);
+ mReceiver.onReceive(mMockContext, intent);
+ assertTrue(instance.isForceAllAppsStandbyEnabled());
+ }
+
static int[] array(int... appIds) {
Arrays.sort(appIds);
return appIds;
diff --git a/services/tests/servicestests/src/com/android/server/am/LaunchingActivityPositionerTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
similarity index 73%
rename from services/tests/servicestests/src/com/android/server/am/LaunchingActivityPositionerTests.java
rename to services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
index 62fa764..f741c70 100644
--- a/services/tests/servicestests/src/com/android/server/am/LaunchingActivityPositionerTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
@@ -17,55 +17,55 @@
package com.android.server.am;
import android.app.ActivityOptions;
-import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
+import com.android.server.am.LaunchParamsController.LaunchParams;
import org.junit.runner.RunWith;
import org.junit.Before;
import org.junit.Test;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_DONE;
+import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.doAnswer;
-import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_SKIP;
+import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
/**
* Tests for exercising resizing bounds due to activity options.
*
* Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.am.LaunchingActivityPositionerTests
+ * atest FrameworksServicesTests:ActivityLaunchParamsModifierTests
*/
@MediumTest
@Presubmit
@RunWith(AndroidJUnit4.class)
-public class LaunchingActivityPositionerTests extends ActivityTestsBase {
- private LaunchingActivityPositioner mPositioner;
+public class ActivityLaunchParamsModifierTests extends ActivityTestsBase {
+ private ActivityLaunchParamsModifier mModifier;
private ActivityManagerService mService;
private ActivityStack mStack;
private TaskRecord mTask;
private ActivityRecord mActivity;
- private Rect mCurrent;
- private Rect mResult;
+ private LaunchParams mCurrent;
+ private LaunchParams mResult;
@Before
@Override
public void setUp() throws Exception {
super.setUp();
mService = createActivityManagerService();
- mPositioner = new LaunchingActivityPositioner(mService.mStackSupervisor);
- mCurrent = new Rect();
- mResult = new Rect();
+ mModifier = new ActivityLaunchParamsModifier(mService.mStackSupervisor);
+ mCurrent = new LaunchParams();
+ mResult = new LaunchParams();
mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
@@ -78,35 +78,35 @@
@Test
public void testSkippedInvocations() throws Exception {
// No specified activity should be ignored
- assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
+ assertEquals(RESULT_SKIP, mModifier.onCalculate(null /*task*/, null /*layout*/,
null /*activity*/, null /*source*/, null /*options*/, mCurrent, mResult));
// No specified activity options should be ignored
- assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
+ assertEquals(RESULT_SKIP, mModifier.onCalculate(null /*task*/, null /*layout*/,
mActivity, null /*source*/, null /*options*/, mCurrent, mResult));
// launch bounds specified should be ignored.
final ActivityOptions options = ActivityOptions.makeBasic();
- assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
+ assertEquals(RESULT_SKIP, mModifier.onCalculate(null /*task*/, null /*layout*/,
mActivity, null /*source*/, options /*options*/, mCurrent, mResult));
// Non-resizeable records should be ignored
mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
assertFalse(mActivity.isResizeable());
- assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
+ assertEquals(RESULT_SKIP, mModifier.onCalculate(null /*task*/, null /*layout*/,
mActivity, null /*source*/, options /*options*/, mCurrent, mResult));
// make record resizeable
mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
assertTrue(mActivity.isResizeable());
- assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
+ assertEquals(RESULT_SKIP, mModifier.onCalculate(null /*task*/, null /*layout*/,
mActivity, null /*source*/, options /*options*/, mCurrent, mResult));
// Does not support freeform
mService.mSupportsFreeformWindowManagement = false;
assertFalse(mService.mStackSupervisor.canUseActivityOptionsLaunchBounds(options));
- assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
+ assertEquals(RESULT_SKIP, mModifier.onCalculate(null /*task*/, null /*layout*/,
mActivity, null /*source*/, options /*options*/, mCurrent, mResult));
mService.mSupportsFreeformWindowManagement = true;
@@ -114,15 +114,15 @@
assertTrue(mService.mStackSupervisor.canUseActivityOptionsLaunchBounds(options));
// Invalid bounds
- assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
+ assertEquals(RESULT_SKIP, mModifier.onCalculate(null /*task*/, null /*layout*/,
mActivity, null /*source*/, options /*options*/, mCurrent, mResult));
options.setLaunchBounds(new Rect(0, 0, -1, -1));
- assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
+ assertEquals(RESULT_SKIP, mModifier.onCalculate(null /*task*/, null /*layout*/,
mActivity, null /*source*/, options /*options*/, mCurrent, mResult));
// Valid bounds should cause the positioner to be applied.
options.setLaunchBounds(new Rect(0, 0, 100, 100));
- assertEquals(RESULT_DONE, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
+ assertEquals(RESULT_DONE, mModifier.onCalculate(null /*task*/, null /*layout*/,
mActivity, null /*source*/, options /*options*/, mCurrent, mResult));
}
@@ -136,8 +136,8 @@
final Rect proposedBounds = new Rect(20, 30, 45, 40);
options.setLaunchBounds(proposedBounds);
- assertEquals(RESULT_DONE, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
+ assertEquals(RESULT_DONE, mModifier.onCalculate(null /*task*/, null /*layout*/,
mActivity, null /*source*/, options /*options*/, mCurrent, mResult));
- assertEquals(mResult, proposedBounds);
+ assertEquals(mResult.mBounds, proposedBounds);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index ee45595..9923fa8 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -21,6 +21,10 @@
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
+import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
+import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
@@ -32,25 +36,29 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-
+import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.PauseActivityItem;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
+import android.util.MutableBoolean;
import org.junit.runner.RunWith;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
/**
* Tests for the {@link ActivityRecord} class.
*
* Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.am.ActivityRecordTests
+ * atest FrameworksServicesTests:com.android.server.am.ActivityRecordTests
*/
@MediumTest
@Presubmit
@@ -101,6 +109,32 @@
}
@Test
+ public void testPausingWhenVisibleFromStopped() throws Exception {
+ final MutableBoolean pauseFound = new MutableBoolean(false);
+ doAnswer((InvocationOnMock invocationOnMock) -> {
+ final ClientTransaction transaction = invocationOnMock.getArgument(0);
+ if (transaction.getLifecycleStateRequest() instanceof PauseActivityItem) {
+ pauseFound.value = true;
+ }
+ return null;
+ }).when(mActivity.app.thread).scheduleTransaction(any());
+ mActivity.state = STOPPED;
+
+ mActivity.makeVisibleIfNeeded(null /* starting */);
+
+ assertEquals(mActivity.state, PAUSING);
+
+ assertTrue(pauseFound.value);
+
+ // Make sure that the state does not change for current non-stopping states.
+ mActivity.state = INITIALIZING;
+
+ mActivity.makeVisibleIfNeeded(null /* starting */);
+
+ assertEquals(mActivity.state, INITIALIZING);
+ }
+
+ @Test
public void testPositionLimitedAspectRatioNavBarBottom() throws Exception {
verifyPositionWithLimitedAspectRatio(NAV_BAR_BOTTOM, new Rect(0, 0, 1000, 2000), 1.5f,
new Rect(0, 0, 1000, 1500));
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index d74d994..96bf49b 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -32,6 +32,7 @@
import org.mockito.invocation.InvocationOnMock;
+import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -177,6 +178,10 @@
mTaskRecord.addActivityToTop(activity);
}
+ activity.setProcess(new ProcessRecord(null, mService.mContext.getApplicationInfo(),
+ "name", 12345));
+ activity.app.thread = mock(IApplicationThread.class);
+
return activity;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ClientLifecycleManagerTests.java b/services/tests/servicestests/src/com/android/server/am/ClientLifecycleManagerTests.java
new file mode 100644
index 0000000..ef6d5e8
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/ClientLifecycleManagerTests.java
@@ -0,0 +1,44 @@
+package com.android.server.am;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.IApplicationThread;
+import android.app.servertransaction.ClientTransaction;
+import android.os.Binder;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class ClientLifecycleManagerTests {
+
+ @Test
+ public void testScheduleAndRecycleBinderClientTransaction() throws Exception {
+ ClientTransaction item = spy(ClientTransaction.obtain(mock(IApplicationThread.class),
+ new Binder()));
+
+ ClientLifecycleManager clientLifecycleManager = new ClientLifecycleManager();
+ clientLifecycleManager.scheduleTransaction(item);
+
+ verify(item, times(1)).recycle();
+ }
+
+ @Test
+ public void testScheduleNoRecycleNonBinderClientTransaction() throws Exception {
+ ClientTransaction item = spy(ClientTransaction.obtain(mock(IApplicationThread.Stub.class),
+ new Binder()));
+
+ ClientLifecycleManager clientLifecycleManager = new ClientLifecycleManager();
+ clientLifecycleManager.scheduleTransaction(item);
+
+ verify(item, times(0)).recycle();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java b/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java
new file mode 100644
index 0000000..161c287
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2017 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 android.app.ActivityOptions;
+import android.content.pm.ActivityInfo.WindowLayout;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.am.LaunchParamsController.LaunchParams;
+import org.junit.runner.RunWith;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.android.server.am.LaunchParamsController.LaunchParamsModifier;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
+import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
+import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+/**
+ * Tests for exercising {@link LaunchParamsController}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:LaunchParamsControllerTests
+ */
+@MediumTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class LaunchParamsControllerTests extends ActivityTestsBase {
+ private ActivityManagerService mService;
+ private LaunchParamsController mController;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mService = createActivityManagerService();
+ mController = new LaunchParamsController(mService);
+ }
+
+ /**
+ * Makes sure positioners get values passed to controller.
+ */
+ @Test
+ public void testArgumentPropagation() {
+ final LaunchParamsModifier
+ positioner = mock(LaunchParamsModifier.class);
+ mController.registerModifier(positioner);
+
+ final ActivityRecord record = new ActivityBuilder(mService).build();
+ final ActivityRecord source = new ActivityBuilder(mService).build();
+ final WindowLayout layout = new WindowLayout(0, 0, 0, 0, 0, 0, 0);
+ final ActivityOptions options = mock(ActivityOptions.class);
+
+ mController.calculate(record.getTask(), layout, record, source, options,
+ new LaunchParams());
+ verify(positioner, times(1)).onCalculate(eq(record.getTask()), eq(layout), eq(record),
+ eq(source), eq(options), any(), any());
+ }
+
+ /**
+ * Ensures positioners further down the chain are not called when RESULT_DONE is returned.
+ */
+ @Test
+ public void testEarlyExit() {
+ final LaunchParamsModifier
+ ignoredPositioner = mock(LaunchParamsModifier.class);
+ final LaunchParamsModifier earlyExitPositioner =
+ (task, layout, activity, source, options, currentParams, outParams) -> RESULT_DONE;
+
+ mController.registerModifier(ignoredPositioner);
+ mController.registerModifier(earlyExitPositioner);
+
+ mController.calculate(null /*task*/, null /*layout*/, null /*activity*/,
+ null /*source*/, null /*options*/, new LaunchParams());
+ verify(ignoredPositioner, never()).onCalculate(any(), any(), any(), any(), any(),
+ any(), any());
+ }
+
+ /**
+ * Ensures that positioners are called in the correct order.
+ */
+ @Test
+ public void testRegistration() {
+ LaunchParamsModifier earlyExitPositioner =
+ new InstrumentedPositioner(RESULT_DONE, new LaunchParams());
+
+ final LaunchParamsModifier firstPositioner = spy(earlyExitPositioner);
+
+ mController.registerModifier(firstPositioner);
+
+ mController.calculate(null /*task*/, null /*layout*/, null /*activity*/,
+ null /*source*/, null /*options*/, new LaunchParams());
+ verify(firstPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), any(),
+ any());
+
+ final LaunchParamsModifier secondPositioner = spy(earlyExitPositioner);
+
+ mController.registerModifier(secondPositioner);
+
+ mController.calculate(null /*task*/, null /*layout*/, null /*activity*/,
+ null /*source*/, null /*options*/, new LaunchParams());
+ verify(firstPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), any(),
+ any());
+ verify(secondPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), any(),
+ any());
+ }
+
+ /**
+ * Makes sure positioners further down the registration chain are called.
+ */
+ @Test
+ public void testPassThrough() {
+ final LaunchParamsModifier
+ positioner1 = mock(LaunchParamsModifier.class);
+ final LaunchParams params = new LaunchParams();
+ params.mWindowingMode = WINDOWING_MODE_FREEFORM;
+ params.mBounds.set(0, 0, 30, 20);
+ params.mPreferredDisplayId = 3;
+
+ final InstrumentedPositioner positioner2 = new InstrumentedPositioner(RESULT_CONTINUE,
+ params);
+
+ mController.registerModifier(positioner1);
+ mController.registerModifier(positioner2);
+
+ mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
+ null /*options*/, new LaunchParams());
+
+ verify(positioner1, times(1)).onCalculate(any(), any(), any(), any(), any(),
+ eq(positioner2.getLaunchParams()), any());
+ }
+
+ /**
+ * Ensures skipped results are not propagated.
+ */
+ @Test
+ public void testSkip() {
+ final LaunchParams params1 = new LaunchParams();
+ params1.mBounds.set(0, 0, 10, 10);
+ final InstrumentedPositioner positioner1 = new InstrumentedPositioner(RESULT_SKIP, params1);
+
+ final LaunchParams params2 = new LaunchParams();
+ params2.mBounds.set(0, 0, 20, 30);
+ final InstrumentedPositioner positioner2 =
+ new InstrumentedPositioner(RESULT_CONTINUE, params2);
+
+ mController.registerModifier(positioner1);
+ mController.registerModifier(positioner2);
+
+ final LaunchParams
+ result = new LaunchParams();
+
+ mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
+ null /*options*/, result);
+
+ assertEquals(result, positioner2.getLaunchParams());
+ }
+
+ /**
+ * Ensures that {@link LaunchParamsModifier} requests specifying display id during
+ * layout are honored.
+ */
+ @Test
+ public void testLayoutTaskPreferredDisplayChange() {
+ final LaunchParams params = new LaunchParams();
+ params.mPreferredDisplayId = 2;
+ final InstrumentedPositioner positioner = new InstrumentedPositioner(RESULT_DONE, params);
+ final TaskRecord task = new TaskBuilder(mService.mStackSupervisor).build();
+
+ mController.registerModifier(positioner);
+
+ doNothing().when(mService).moveStackToDisplay(anyInt(), anyInt());
+ mController.layoutTask(task, null /* windowLayout */);
+ verify(mService, times(1)).moveStackToDisplay(eq(task.getStackId()),
+ eq(params.mPreferredDisplayId));
+ }
+
+ /**
+ * Ensures that {@link LaunchParamsModifier} requests specifying windowingMode during
+ * layout are honored.
+ */
+ @Test
+ public void testLayoutTaskWindowingModeChange() {
+ final LaunchParams params = new LaunchParams();
+ final int windowingMode = WINDOWING_MODE_FREEFORM;
+ params.mWindowingMode = windowingMode;
+ final InstrumentedPositioner positioner = new InstrumentedPositioner(RESULT_DONE, params);
+ final TaskRecord task = new TaskBuilder(mService.mStackSupervisor).build();
+
+ mController.registerModifier(positioner);
+
+ final int beforeWindowMode = task.getStack().getWindowingMode();
+ assertNotEquals(beforeWindowMode, windowingMode);
+
+ mController.layoutTask(task, null /* windowLayout */);
+
+ final int afterWindowMode = task.getStack().getWindowingMode();
+ assertEquals(afterWindowMode, windowingMode);
+ }
+
+ public static class InstrumentedPositioner implements
+ LaunchParamsModifier {
+
+ final private int mReturnVal;
+ final private LaunchParams mParams;
+
+ InstrumentedPositioner(int returnVal, LaunchParams params) {
+ mReturnVal = returnVal;
+ mParams = params;
+ }
+
+ @Override
+ public int onCalculate(TaskRecord task, WindowLayout layout, ActivityRecord activity,
+ ActivityRecord source, ActivityOptions options,
+ LaunchParams currentParams, LaunchParams outParams) {
+ outParams.set(mParams);
+ return mReturnVal;
+ }
+
+ LaunchParams getLaunchParams() {
+ return mParams;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/LaunchingBoundsControllerTests.java b/services/tests/servicestests/src/com/android/server/am/LaunchingBoundsControllerTests.java
deleted file mode 100644
index 0715174..0000000
--- a/services/tests/servicestests/src/com/android/server/am/LaunchingBoundsControllerTests.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (C) 2017 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 android.app.ActivityOptions;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ActivityInfo.WindowLayout;
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.MediumTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.runner.RunWith;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_DONE;
-import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_CONTINUE;
-import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_SKIP;
-
-import static org.junit.Assert.assertEquals;
-
-/**
- * Tests for exercising {@link LaunchingBoundsController}.
- *
- * Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.am.LaunchingBoundsControllerTests
- */
-@MediumTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class LaunchingBoundsControllerTests extends ActivityTestsBase {
- private LaunchingBoundsController mController;
-
- @Before
- @Override
- public void setUp() throws Exception {
- super.setUp();
- mController = new LaunchingBoundsController();
- }
-
- /**
- * Makes sure positioners get values passed to controller.
- */
- @Test
- public void testArgumentPropagation() {
- final ActivityManagerService service = createActivityManagerService();
- final LaunchingBoundsPositioner positioner = mock(LaunchingBoundsPositioner.class);
- mController.registerPositioner(positioner);
-
- final ActivityRecord record = new ActivityBuilder(service).build();
- final ActivityRecord source = new ActivityBuilder(service).build();
- final WindowLayout layout = new WindowLayout(0, 0, 0, 0, 0, 0, 0);
- final ActivityOptions options = mock(ActivityOptions.class);
-
- mController.calculateBounds(record.getTask(), layout, record, source, options, new Rect());
- verify(positioner, times(1)).onCalculateBounds(eq(record.getTask()), eq(layout), eq(record),
- eq(source), eq(options), any(), any());
- }
-
- /**
- * Ensures positioners further down the chain are not called when RESULT_DONE is returned.
- */
- @Test
- public void testEarlyExit() {
- final LaunchingBoundsPositioner ignoredPositioner = mock(LaunchingBoundsPositioner.class);
- final LaunchingBoundsPositioner earlyExitPositioner =
- (task, layout, activity, source, options, current, result) -> RESULT_DONE;
-
- mController.registerPositioner(ignoredPositioner);
- mController.registerPositioner(earlyExitPositioner);
-
- mController.calculateBounds(null /*task*/, null /*layout*/, null /*activity*/,
- null /*source*/, null /*options*/, new Rect());
- verify(ignoredPositioner, never()).onCalculateBounds(any(), any(), any(), any(), any(),
- any(), any());
- }
-
- /**
- * Ensures that positioners are called in the correct order.
- */
- @Test
- public void testRegistration() {
- LaunchingBoundsPositioner earlyExitPositioner =
- new InstrumentedPositioner(RESULT_DONE, new Rect());
-
- final LaunchingBoundsPositioner firstPositioner = spy(earlyExitPositioner);
-
- mController.registerPositioner(firstPositioner);
-
- mController.calculateBounds(null /*task*/, null /*layout*/, null /*activity*/,
- null /*source*/, null /*options*/, new Rect());
- verify(firstPositioner, times(1)).onCalculateBounds(any(), any(), any(), any(), any(),
- any(), any());
-
- final LaunchingBoundsPositioner secondPositioner = spy(earlyExitPositioner);
-
- mController.registerPositioner(secondPositioner);
-
- mController.calculateBounds(null /*task*/, null /*layout*/, null /*activity*/,
- null /*source*/, null /*options*/, new Rect());
- verify(firstPositioner, times(1)).onCalculateBounds(any(), any(), any(), any(), any(),
- any(), any());
- verify(secondPositioner, times(1)).onCalculateBounds(any(), any(), any(), any(), any(),
- any(), any());
- }
-
- /**
- * Makes sure positioners further down the registration chain are called.
- */
- @Test
- public void testPassThrough() {
- final LaunchingBoundsPositioner positioner1 = mock(LaunchingBoundsPositioner.class);
- final InstrumentedPositioner positioner2 = new InstrumentedPositioner(RESULT_CONTINUE,
- new Rect (0, 0, 30, 20));
-
- mController.registerPositioner(positioner1);
- mController.registerPositioner(positioner2);
-
- mController.calculateBounds(null /*task*/, null /*layout*/, null /*activity*/,
- null /*source*/, null /*options*/, new Rect());
-
- verify(positioner1, times(1)).onCalculateBounds(any(), any(), any(), any(), any(),
- eq(positioner2.getLaunchBounds()), any());
- }
-
- /**
- * Ensures skipped results are not propagated.
- */
- @Test
- public void testSkip() {
- final InstrumentedPositioner positioner1 =
- new InstrumentedPositioner(RESULT_SKIP, new Rect(0, 0, 10, 10));
-
-
- final InstrumentedPositioner positioner2 =
- new InstrumentedPositioner(RESULT_CONTINUE, new Rect(0, 0, 20, 30));
-
- mController.registerPositioner(positioner1);
- mController.registerPositioner(positioner2);
-
- final Rect resultBounds = new Rect();
-
- mController.calculateBounds(null /*task*/, null /*layout*/, null /*activity*/,
- null /*source*/, null /*options*/, resultBounds);
-
- assertEquals(resultBounds, positioner2.getLaunchBounds());
- }
-
- public static class InstrumentedPositioner implements LaunchingBoundsPositioner {
- private int mReturnVal;
- private Rect mBounds;
- InstrumentedPositioner(int returnVal, Rect bounds) {
- mReturnVal = returnVal;
- mBounds = bounds;
- }
-
- @Override
- public int onCalculateBounds(TaskRecord task, ActivityInfo.WindowLayout layout,
- ActivityRecord activity, ActivityRecord source,
- ActivityOptions options, Rect current, Rect result) {
- result.set(mBounds);
- return mReturnVal;
- }
-
- Rect getLaunchBounds() {
- return mBounds;
- }
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/LaunchingTaskPositionerTests.java b/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
similarity index 81%
rename from services/tests/servicestests/src/com/android/server/am/LaunchingTaskPositionerTests.java
rename to services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
index 13daf3e..3d323f0 100644
--- a/services/tests/servicestests/src/com/android/server/am/LaunchingTaskPositionerTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
@@ -23,6 +23,7 @@
import android.support.test.runner.AndroidJUnit4;
import android.view.Gravity;
+
import org.junit.runner.RunWith;
import org.junit.Before;
import org.junit.Test;
@@ -32,7 +33,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_CONTINUE;
+import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
@@ -42,15 +43,15 @@
/**
- * Tests for exercising resizing bounds.
+ * Tests for exercising resizing task bounds.
*
* Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.am.LaunchingTaskPositionerTests
+ * atest FrameworksServicesTests:TaskLaunchParamsModifierTests
*/
@MediumTest
@Presubmit
@RunWith(AndroidJUnit4.class)
-public class LaunchingTaskPositionerTests extends ActivityTestsBase {
+public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
private final static int STACK_WIDTH = 100;
private final static int STACK_HEIGHT = 200;
@@ -60,10 +61,10 @@
private ActivityStack mStack;
private TaskRecord mTask;
- private LaunchingTaskPositioner mPositioner;
+ private TaskLaunchParamsModifier mPositioner;
- private Rect mCurrent;
- private Rect mResult;
+ private LaunchParamsController.LaunchParams mCurrent;
+ private LaunchParamsController.LaunchParams mResult;
@Before
@Override
@@ -79,10 +80,10 @@
// dimensions on resize.
mTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build();
- mPositioner = new LaunchingTaskPositioner();
+ mPositioner = new TaskLaunchParamsModifier();
- mResult = new Rect();
- mCurrent = new Rect();
+ mResult = new LaunchParamsController.LaunchParams();
+ mCurrent = new LaunchParamsController.LaunchParams();
}
/**
@@ -103,9 +104,9 @@
*/
@Test
public void testLaunchNoWindowLayout() throws Exception {
- assertEquals(RESULT_CONTINUE, mPositioner.onCalculateBounds(mTask, null /*layout*/,
+ assertEquals(RESULT_CONTINUE, mPositioner.onCalculate(mTask, null /*layout*/,
null /*record*/, null /*source*/, null /*options*/, mCurrent, mResult));
- assertEquals(getDefaultBounds(Gravity.NO_GRAVITY), mResult);
+ assertEquals(getDefaultBounds(Gravity.NO_GRAVITY), mResult.mBounds);
}
/**
@@ -115,10 +116,10 @@
*/
@Test
public void testlaunchEmptyWindowLayout() throws Exception {
- assertEquals(RESULT_CONTINUE, mPositioner.onCalculateBounds(mTask,
+ assertEquals(RESULT_CONTINUE, mPositioner.onCalculate(mTask,
new WindowLayout(0, 0, 0, 0, Gravity.NO_GRAVITY, 0, 0), null /*activity*/,
null /*source*/, null /*options*/, mCurrent, mResult));
- assertEquals(mResult, getDefaultBounds(Gravity.NO_GRAVITY));
+ assertEquals(mResult.mBounds, getDefaultBounds(Gravity.NO_GRAVITY));
}
/**
@@ -148,13 +149,13 @@
private void testGravity(int gravity) {
try {
- assertEquals(RESULT_CONTINUE, mPositioner.onCalculateBounds(mTask,
+ assertEquals(RESULT_CONTINUE, mPositioner.onCalculate(mTask,
new WindowLayout(0, 0, 0, 0, gravity, 0, 0), null /*activity*/,
null /*source*/, null /*options*/, mCurrent, mResult));
- assertEquals(mResult, getDefaultBounds(gravity));
+ assertEquals(mResult.mBounds, getDefaultBounds(gravity));
} finally {
- mCurrent.setEmpty();
- mResult.setEmpty();
+ mCurrent.reset();
+ mResult.reset();
}
}
@@ -178,7 +179,7 @@
final WindowLayout layout = new WindowLayout(0, 0, 0, 0, gravity, 0, 0);
// layout first task
- mService.mStackSupervisor.getLaunchingBoundsController().layoutTask(mTask, layout);
+ mService.mStackSupervisor.getLaunchParamsController().layoutTask(mTask, layout);
// Second task will be laid out on top of the first so starting bounds is the same.
final Rect expectedBounds = new Rect(mTask.getOverrideBounds());
@@ -196,25 +197,25 @@
// layout second task
assertEquals(RESULT_CONTINUE,
- mPositioner.onCalculateBounds(secondTask, layout, null /*activity*/,
+ mPositioner.onCalculate(secondTask, layout, null /*activity*/,
null /*source*/, null /*options*/, mCurrent, mResult));
if ((gravity & (Gravity.TOP | Gravity.RIGHT)) == (Gravity.TOP | Gravity.RIGHT)
|| (gravity & (Gravity.BOTTOM | Gravity.RIGHT))
== (Gravity.BOTTOM | Gravity.RIGHT)) {
- expectedBounds.offset(-LaunchingTaskPositioner.getHorizontalStep(
+ expectedBounds.offset(-TaskLaunchParamsModifier.getHorizontalStep(
mStack.getOverrideBounds()), 0);
} else if ((gravity & Gravity.TOP) == Gravity.TOP
|| (gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
expectedBounds.offset(
- LaunchingTaskPositioner.getHorizontalStep(mStack.getOverrideBounds()), 0);
+ TaskLaunchParamsModifier.getHorizontalStep(mStack.getOverrideBounds()), 0);
} else {
expectedBounds.offset(
- LaunchingTaskPositioner.getHorizontalStep(mStack.getOverrideBounds()),
- LaunchingTaskPositioner.getVerticalStep(mStack.getOverrideBounds()));
+ TaskLaunchParamsModifier.getHorizontalStep(mStack.getOverrideBounds()),
+ TaskLaunchParamsModifier.getVerticalStep(mStack.getOverrideBounds()));
}
- assertEquals(mResult, expectedBounds);
+ assertEquals(mResult.mBounds, expectedBounds);
} finally {
// Remove task and activity to prevent influencing future tests
if (activity != null) {
@@ -232,9 +233,9 @@
bounds.set(mStack.getOverrideBounds());
final int verticalInset =
- LaunchingTaskPositioner.getFreeformStartTop(mStack.getOverrideBounds());
+ TaskLaunchParamsModifier.getFreeformStartTop(mStack.getOverrideBounds());
final int horizontalInset =
- LaunchingTaskPositioner.getFreeformStartLeft(mStack.getOverrideBounds());
+ TaskLaunchParamsModifier.getFreeformStartLeft(mStack.getOverrideBounds());
bounds.inset(horizontalInset, verticalInset);
diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
deleted file mode 100644
index 766d30d..0000000
--- a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * Copyright (C) 2017 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.backup;
-
-import static org.mockito.ArgumentMatchers.any;
-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.spy;
-import static org.mockito.Mockito.verify;
-import static org.testng.Assert.expectThrows;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.Intent;
-import android.os.HandlerThread;
-import android.platform.test.annotations.Presubmit;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.util.Log;
-
-import org.junit.After;
-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;
-
-@SmallTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-// TODO: Migrate this to Robolectric and merge with BackupManagerServiceRoboTest (and remove 'Robo')
-public class BackupManagerServiceTest {
- private static final String TAG = "BMSTest";
- private static final ComponentName TRANSPORT_COMPONENT =
- new ComponentName(
- "com.google.android.gms",
- "com.google.android.gms.backup.BackupTransportService");
- private static final String TRANSPORT_NAME = TRANSPORT_COMPONENT.flattenToShortString();
-
- @Mock private TransportManager mTransportManager;
- private Context mContext;
- private HandlerThread mBackupThread;
- private int mPackageUid;
- private File mBaseStateDir;
- private File mDataDir;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- Context baseContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
- mContext = spy(new ContextWrapper(baseContext));
-
- mBackupThread = new HandlerThread("backup-test");
- mBackupThread.setUncaughtExceptionHandler(
- (t, e) -> Log.e(TAG, "Uncaught exception in test thread " + t.getName(), e));
- mBackupThread.start();
-
- File cacheDir = mContext.getCacheDir();
- mBaseStateDir = new File(cacheDir, "base_state_dir");
- mDataDir = new File(cacheDir, "data_dir");
-
- mPackageUid =
- mContext.getPackageManager().getPackageUid(TRANSPORT_COMPONENT.getPackageName(), 0);
- }
-
- @After
- public void tearDown() throws Exception {
- mBackupThread.quit();
- mBaseStateDir.delete();
- mDataDir.delete();
- }
-
- @Test
- public void testConstructor_callsTransportManagerSetTransportBoundListener() throws Exception {
- createBackupManagerService();
-
- verify(mTransportManager)
- .setTransportBoundListener(any(TransportManager.TransportBoundListener.class));
- }
-
- @Test
- public void
- testUpdateTransportAttributes_whenTransportUidEqualsToCallingUid_callsThroughToTransportManager()
- throws Exception {
- grantBackupPermission();
- RefactoredBackupManagerService backupManagerService = createBackupManagerService();
- Intent configurationIntent = new Intent();
- Intent dataManagementIntent = new Intent();
-
- backupManagerService.updateTransportAttributes(
- mPackageUid,
- TRANSPORT_COMPONENT,
- TRANSPORT_NAME,
- configurationIntent,
- "currentDestinationString",
- dataManagementIntent,
- "dataManagementLabel");
-
- verify(mTransportManager)
- .updateTransportAttributes(
- eq(TRANSPORT_COMPONENT),
- eq(TRANSPORT_NAME),
- eq(configurationIntent),
- eq("currentDestinationString"),
- eq(dataManagementIntent),
- eq("dataManagementLabel"));
- }
-
- @Test
- public void testUpdateTransportAttributes_whenTransportUidNotEqualToCallingUid_throwsException()
- throws Exception {
- grantBackupPermission();
- RefactoredBackupManagerService backupManagerService = createBackupManagerService();
-
- expectThrows(
- SecurityException.class,
- () ->
- backupManagerService.updateTransportAttributes(
- mPackageUid + 1,
- TRANSPORT_COMPONENT,
- TRANSPORT_NAME,
- new Intent(),
- "currentDestinationString",
- new Intent(),
- "dataManagementLabel"));
- }
-
- @Test
- public void testUpdateTransportAttributes_whenTransportComponentNull_throwsException() {
- grantBackupPermission();
- RefactoredBackupManagerService backupManagerService = createBackupManagerService();
-
- expectThrows(
- RuntimeException.class,
- () ->
- backupManagerService.updateTransportAttributes(
- mPackageUid,
- null,
- TRANSPORT_NAME,
- new Intent(),
- "currentDestinationString",
- new Intent(),
- "dataManagementLabel"));
- }
-
- @Test
- public void testUpdateTransportAttributes_whenNameNull_throwsException() {
- grantBackupPermission();
- RefactoredBackupManagerService backupManagerService = createBackupManagerService();
-
- expectThrows(
- RuntimeException.class,
- () ->
- backupManagerService.updateTransportAttributes(
- mPackageUid,
- TRANSPORT_COMPONENT,
- null,
- new Intent(),
- "currentDestinationString",
- new Intent(),
- "dataManagementLabel"));
- }
-
- @Test
- public void testUpdateTransportAttributes_whenCurrentDestinationStringNull_throwsException() {
- grantBackupPermission();
- RefactoredBackupManagerService backupManagerService = createBackupManagerService();
-
- expectThrows(
- RuntimeException.class,
- () ->
- backupManagerService.updateTransportAttributes(
- mPackageUid,
- TRANSPORT_COMPONENT,
- TRANSPORT_NAME,
- new Intent(),
- null,
- new Intent(),
- "dataManagementLabel"));
- }
-
- @Test
- public void
- testUpdateTransportAttributes_whenDataManagementArgumentsNullityDontMatch_throwsException() {
- grantBackupPermission();
- RefactoredBackupManagerService backupManagerService = createBackupManagerService();
-
- expectThrows(
- RuntimeException.class,
- () ->
- backupManagerService.updateTransportAttributes(
- mPackageUid,
- TRANSPORT_COMPONENT,
- TRANSPORT_NAME,
- new Intent(),
- "currentDestinationString",
- null,
- "dataManagementLabel"));
-
- expectThrows(
- RuntimeException.class,
- () ->
- backupManagerService.updateTransportAttributes(
- mPackageUid,
- TRANSPORT_COMPONENT,
- TRANSPORT_NAME,
- new Intent(),
- "currentDestinationString",
- new Intent(),
- null));
- }
-
- @Test
- public void
- testUpdateTransportAttributes_whenDataManagementArgumentsNull_callsThroughToTransportManager() {
- grantBackupPermission();
- RefactoredBackupManagerService backupManagerService = createBackupManagerService();
- Intent configurationIntent = new Intent();
-
- backupManagerService.updateTransportAttributes(
- mPackageUid,
- TRANSPORT_COMPONENT,
- TRANSPORT_NAME,
- configurationIntent,
- "currentDestinationString",
- null,
- null);
-
- verify(mTransportManager)
- .updateTransportAttributes(
- eq(TRANSPORT_COMPONENT),
- eq(TRANSPORT_NAME),
- eq(configurationIntent),
- eq("currentDestinationString"),
- eq(null),
- eq(null));
- }
-
- @Test
- public void
- testUpdateTransportAttributes_whenPermissionGranted_callsThroughToTransportManager() {
- grantBackupPermission();
- RefactoredBackupManagerService backupManagerService = createBackupManagerService();
- Intent configurationIntent = new Intent();
- Intent dataManagementIntent = new Intent();
-
- backupManagerService.updateTransportAttributes(
- mPackageUid,
- TRANSPORT_COMPONENT,
- TRANSPORT_NAME,
- configurationIntent,
- "currentDestinationString",
- dataManagementIntent,
- "dataManagementLabel");
-
- verify(mTransportManager)
- .updateTransportAttributes(
- eq(TRANSPORT_COMPONENT),
- eq(TRANSPORT_NAME),
- eq(configurationIntent),
- eq("currentDestinationString"),
- eq(dataManagementIntent),
- eq("dataManagementLabel"));
- }
-
- @Test
- public void testUpdateTransportAttributes_whenPermissionDenied_throwsSecurityException() {
- denyBackupPermission();
- RefactoredBackupManagerService backupManagerService = createBackupManagerService();
-
- expectThrows(
- SecurityException.class,
- () ->
- backupManagerService.updateTransportAttributes(
- mPackageUid,
- TRANSPORT_COMPONENT,
- TRANSPORT_NAME,
- new Intent(),
- "currentDestinationString",
- new Intent(),
- "dataManagementLabel"));
- }
-
- private RefactoredBackupManagerService createBackupManagerService() {
- return new RefactoredBackupManagerService(
- mContext,
- new Trampoline(mContext),
- mBackupThread,
- mBaseStateDir,
- mDataDir,
- mTransportManager);
- }
-
- private void grantBackupPermission() {
- doNothing().when(mContext).enforceCallingOrSelfPermission(any(), anyString());
- }
-
- private void denyBackupPermission() {
- doThrow(new SecurityException())
- .when(mContext)
- .enforceCallingOrSelfPermission(
- eq(android.Manifest.permission.BACKUP), anyString());
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 0650acb..5134f52 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -15,11 +15,13 @@
*/
package com.android.server.devicepolicy;
+import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.IActivityManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.backup.IBackupManager;
+import android.app.usage.UsageStatsManagerInternal;
import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageManager;
@@ -152,6 +154,11 @@
}
@Override
+ UsageStatsManagerInternal getUsageStatsManagerInternal() {
+ return services.usageStatsManagerInternal;
+ }
+
+ @Override
PackageManagerInternal getPackageManagerInternal() {
return services.packageManagerInternal;
}
@@ -182,6 +189,11 @@
}
@Override
+ ActivityManagerInternal getActivityManagerInternal() {
+ return services.activityManagerInternal;
+ }
+
+ @Override
IPackageManager getIPackageManager() {
return services.ipackageManager;
}
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 58ac7d2..1df0ff2 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -44,6 +44,7 @@
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
@@ -86,10 +87,12 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.devicepolicy.DevicePolicyManagerService.RestrictionsListener;
import com.android.server.pm.UserRestrictionsUtils;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
+import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@@ -182,6 +185,7 @@
initializeDpms();
+ Mockito.reset(getServices().usageStatsManagerInternal);
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID);
setUpPackageManagerForAdmin(admin3, DpmMockContext.CALLER_UID);
@@ -207,6 +211,7 @@
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
+ dpms.handleStart();
dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY);
dpms.systemReady(SystemService.PHASE_BOOT_COMPLETED);
@@ -278,6 +283,32 @@
assertNull(LocalServices.getService(DevicePolicyManagerInternal.class));
}
+ public void testHandleStart() throws Exception {
+ // Device owner in SYSTEM_USER
+ setDeviceOwner();
+ // Profile owner in CALLER_USER_HANDLE
+ setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID);
+ setAsProfileOwner(admin2);
+ // Active admin in CALLER_USER_HANDLE
+ final int ANOTHER_UID = UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE, 1306);
+ setUpPackageManagerForFakeAdmin(adminAnotherPackage, ANOTHER_UID, admin2);
+ dpm.setActiveAdmin(adminAnotherPackage, /* replace =*/ false,
+ DpmMockContext.CALLER_USER_HANDLE);
+ assertTrue(dpm.isAdminActiveAsUser(adminAnotherPackage,
+ DpmMockContext.CALLER_USER_HANDLE));
+
+ initializeDpms();
+
+ // Verify
+ verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+ MockUtils.checkAdminApps(admin1.getPackageName()),
+ eq(UserHandle.USER_SYSTEM));
+ verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+ MockUtils.checkAdminApps(admin2.getPackageName(),
+ adminAnotherPackage.getPackageName()),
+ eq(DpmMockContext.CALLER_USER_HANDLE));
+ }
+
/**
* Caller doesn't have proper permissions.
*/
@@ -330,6 +361,9 @@
eq(DpmMockContext.CALLER_USER_HANDLE),
anyString());
+ verify(getServices().usageStatsManagerInternal).onActiveAdminAdded(
+ admin1.getPackageName(), DpmMockContext.CALLER_USER_HANDLE);
+
// TODO Verify other calls too.
// Make sure it's active admin1.
@@ -369,6 +403,11 @@
eq(DpmMockContext.CALLER_USER_HANDLE),
anyString());
+ // times(2) because it was previously called for admin1 which is in the same package
+ // as admin2.
+ verify(getServices().usageStatsManagerInternal, times(2)).onActiveAdminAdded(
+ admin2.getPackageName(), DpmMockContext.CALLER_USER_HANDLE);
+
// 4. Add the same admin1 again without replace, which should throw.
assertExpectException(IllegalArgumentException.class, /* messageRegex= */ null,
() -> dpm.setActiveAdmin(admin1, /* replace =*/ false));
@@ -384,6 +423,10 @@
assertEquals(admin1, admins.get(0));
assertEquals(admin2, admins.get(1));
+ // There shouldn't be any callback to UsageStatsManagerInternal when the admin is being
+ // replaced
+ verifyNoMoreInteractions(getServices().usageStatsManagerInternal);
+
// Another user has no admins.
mContext.callerPermissions.add("android.permission.INTERACT_ACROSS_USERS_FULL");
@@ -516,6 +559,8 @@
() -> dpm.removeActiveAdmin(admin1));
assertFalse(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+ verify(getServices().usageStatsManagerInternal, times(0)).setActiveAdminApps(
+ null, DpmMockContext.CALLER_USER_HANDLE);
// 2. User unlocked.
when(getServices().userManager.isUserUnlocked(eq(DpmMockContext.CALLER_USER_HANDLE)))
@@ -523,6 +568,8 @@
dpm.removeActiveAdmin(admin1);
assertFalse(dpm.isAdminActiveAsUser(admin1, DpmMockContext.CALLER_USER_HANDLE));
+ verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+ null, DpmMockContext.CALLER_USER_HANDLE);
}
/**
@@ -547,6 +594,8 @@
dpms.removeActiveAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE);
assertFalse(dpm.isAdminActiveAsUser(admin1, DpmMockContext.CALLER_USER_HANDLE));
+ verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+ null, DpmMockContext.CALLER_USER_HANDLE);
// TODO DO Still can't be removed in this case.
}
@@ -588,6 +637,8 @@
isNull(Bundle.class));
assertFalse(dpm.isAdminActiveAsUser(admin1, DpmMockContext.CALLER_USER_HANDLE));
+ verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+ null, DpmMockContext.CALLER_USER_HANDLE);
// Again broadcast from saveSettingsLocked().
verify(mContext.spiedContext, times(2)).sendBroadcastAsUser(
@@ -598,6 +649,86 @@
// TODO Check other internal calls.
}
+ public void testRemoveActiveAdmin_multipleAdminsInUser() {
+ // Need MANAGE_DEVICE_ADMINS for setActiveAdmin. We'll remove it later.
+ mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+
+ // Add admin1.
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+
+ assertTrue(dpm.isAdminActive(admin1));
+ assertFalse(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+
+ // Add admin2.
+ dpm.setActiveAdmin(admin2, /* replace =*/ false);
+
+ assertTrue(dpm.isAdminActive(admin2));
+ assertFalse(dpm.isRemovingAdmin(admin2, DpmMockContext.CALLER_USER_HANDLE));
+
+ // Broadcast from saveSettingsLocked().
+ verify(mContext.spiedContext, times(2)).sendBroadcastAsUser(
+ MockUtils.checkIntentAction(
+ DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+ MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+
+ // Remove. No permissions, but same user, so it'll work.
+ mContext.callerPermissions.clear();
+ dpm.removeActiveAdmin(admin1);
+
+ verify(mContext.spiedContext).sendOrderedBroadcastAsUser(
+ MockUtils.checkIntentAction(
+ DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED),
+ MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE),
+ isNull(String.class),
+ any(BroadcastReceiver.class),
+ eq(dpms.mHandler),
+ eq(Activity.RESULT_OK),
+ isNull(String.class),
+ isNull(Bundle.class));
+
+ assertFalse(dpm.isAdminActiveAsUser(admin1, DpmMockContext.CALLER_USER_HANDLE));
+ verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+ MockUtils.checkAdminApps(admin2.getPackageName()),
+ eq(DpmMockContext.CALLER_USER_HANDLE));
+
+ // Again broadcast from saveSettingsLocked().
+ verify(mContext.spiedContext, times(3)).sendBroadcastAsUser(
+ MockUtils.checkIntentAction(
+ DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+ MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+ }
+
+ /**
+ * Test for:
+ * {@link DevicePolicyManager#forceRemoveActiveAdmin(ComponentName, int)}
+ */
+ public void testForceRemoveActiveAdmin() throws Exception {
+ mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+
+ // Add admin.
+ setupPackageInPackageManager(admin1.getPackageName(),
+ /* userId= */ DpmMockContext.CALLER_USER_HANDLE,
+ /* appId= */ 10138,
+ /* flags= */ ApplicationInfo.FLAG_TEST_ONLY);
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+ assertTrue(dpm.isAdminActive(admin1));
+
+ // Calling from a non-shell uid should fail with a SecurityException
+ mContext.binder.callingUid = 123456;
+ assertExpectException(SecurityException.class,
+ /* messageRegex =*/ "Non-shell user attempted to call",
+ () -> dpms.forceRemoveActiveAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+
+ mContext.binder.callingUid = Process.SHELL_UID;
+ dpms.forceRemoveActiveAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE);
+
+ mContext.callerPermissions.add(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ // Verify
+ assertFalse(dpm.isAdminActiveAsUser(admin1, DpmMockContext.CALLER_USER_HANDLE));
+ verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+ null, DpmMockContext.CALLER_USER_HANDLE);
+ }
+
/**
* Test for: @{link DevicePolicyManager#setActivePasswordState}
*
@@ -954,6 +1085,9 @@
eq(null),
eq(true), eq(CAMERA_NOT_DISABLED));
+ verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+ null, UserHandle.USER_SYSTEM);
+
assertFalse(dpm.isAdminActiveAsUser(admin1, UserHandle.USER_SYSTEM));
// ACTION_DEVICE_OWNER_CHANGED should be sent twice, once for setting the device owner
@@ -1044,6 +1178,8 @@
// Check
assertFalse(dpm.isProfileOwnerApp(admin1.getPackageName()));
assertFalse(dpm.isAdminActiveAsUser(admin1, DpmMockContext.CALLER_USER_HANDLE));
+ verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+ null, DpmMockContext.CALLER_USER_HANDLE);
}
public void testSetProfileOwner_failures() throws Exception {
@@ -3885,6 +4021,8 @@
dpm.setPasswordMinimumNumeric(admin1, 1);
dpm.setPasswordMinimumSymbols(admin1, 0);
+ reset(mContext.spiedContext);
+
PasswordMetrics passwordMetricsNoSymbols = new PasswordMetrics(
DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, 9,
8, 2,
@@ -3934,9 +4072,16 @@
dpm.setActivePasswordState(passwordMetrics, userHandle);
dpm.reportPasswordChanged(userHandle);
+ // Drain ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED broadcasts as part of
+ // reportPasswordChanged()
+ verify(mContext.spiedContext, times(3)).sendBroadcastAsUser(
+ MockUtils.checkIntentAction(
+ DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+ MockUtils.checkUserHandle(userHandle));
+
final Intent intent = new Intent(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED);
intent.setComponent(admin1);
- intent.putExtra(Intent.EXTRA_USER, UserHandle.of(mContext.binder.callingUid));
+ intent.putExtra(Intent.EXTRA_USER, UserHandle.of(userHandle));
verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
MockUtils.checkIntent(intent),
@@ -4347,6 +4492,45 @@
verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(null, caller);
}
+ public void testDisallowSharingIntoProfileSetRestriction() {
+ Bundle restriction = new Bundle();
+ restriction.putBoolean(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, true);
+
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ RestrictionsListener listener = new RestrictionsListener(mContext);
+ listener.onUserRestrictionsChanged(DpmMockContext.CALLER_USER_HANDLE, restriction,
+ new Bundle());
+ verifyDataSharingChangedBroadcast();
+ }
+
+ public void testDisallowSharingIntoProfileClearRestriction() {
+ Bundle restriction = new Bundle();
+ restriction.putBoolean(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, true);
+
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ RestrictionsListener listener = new RestrictionsListener(mContext);
+ listener.onUserRestrictionsChanged(DpmMockContext.CALLER_USER_HANDLE, new Bundle(),
+ restriction);
+ verifyDataSharingChangedBroadcast();
+ }
+
+ public void testDisallowSharingIntoProfileUnchanged() {
+ RestrictionsListener listener = new RestrictionsListener(mContext);
+ listener.onUserRestrictionsChanged(DpmMockContext.CALLER_USER_HANDLE, new Bundle(),
+ new Bundle());
+ verify(mContext.spiedContext, never()).sendBroadcastAsUser(any(), any());
+ }
+
+ private void verifyDataSharingChangedBroadcast() {
+ Intent expectedIntent = new Intent(
+ DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_CHANGED);
+ expectedIntent.setPackage("com.android.managedprovisioning");
+ expectedIntent.putExtra(Intent.EXTRA_USER_ID, DpmMockContext.CALLER_USER_HANDLE);
+ verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
+ MockUtils.checkIntent(expectedIntent),
+ MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+ }
+
private void verifyCanGetOwnerInstalledCaCerts(
final ComponentName caller, final DpmMockContext callerContext) throws Exception {
final String alias = "cert";
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 4232c44..268d424 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -25,10 +25,12 @@
import android.accounts.Account;
import android.accounts.AccountManager;
+import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.IActivityManager;
import android.app.NotificationManager;
import android.app.backup.IBackupManager;
+import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -73,6 +75,7 @@
public final SystemPropertiesForMock systemProperties;
public final UserManager userManager;
public final UserManagerInternal userManagerInternal;
+ public final UsageStatsManagerInternal usageStatsManagerInternal;
public final PackageManagerInternal packageManagerInternal;
public final UserManagerForMock userManagerForMock;
public final PowerManagerForMock powerManager;
@@ -82,6 +85,7 @@
public final IIpConnectivityMetrics iipConnectivityMetrics;
public final IWindowManager iwindowManager;
public final IActivityManager iactivityManager;
+ public ActivityManagerInternal activityManagerInternal;
public final IPackageManager ipackageManager;
public final IBackupManager ibackupManager;
public final IAudioService iaudioService;
@@ -108,6 +112,7 @@
systemProperties = mock(SystemPropertiesForMock.class);
userManager = mock(UserManager.class);
userManagerInternal = mock(UserManagerInternal.class);
+ usageStatsManagerInternal = mock(UsageStatsManagerInternal.class);
userManagerForMock = mock(UserManagerForMock.class);
packageManagerInternal = mock(PackageManagerInternal.class);
powerManager = mock(PowerManagerForMock.class);
@@ -117,6 +122,7 @@
iipConnectivityMetrics = mock(IIpConnectivityMetrics.class);
iwindowManager = mock(IWindowManager.class);
iactivityManager = mock(IActivityManager.class);
+ activityManagerInternal = mock(ActivityManagerInternal.class);
ipackageManager = mock(IPackageManager.class);
ibackupManager = mock(IBackupManager.class);
iaudioService = mock(IAudioService.class);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
index e43786c..dec962e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
@@ -24,13 +24,16 @@
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
+import android.util.ArraySet;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
-import org.mockito.Mockito;
import org.mockito.hamcrest.MockitoHamcrest;
+import java.util.Arrays;
+import java.util.Set;
+
public class MockUtils {
private MockUtils() {
}
@@ -88,7 +91,8 @@
@Override
public boolean matches(Object item) {
if (item == null) return false;
- return intent.filterEquals((Intent) item);
+ if (!intent.filterEquals((Intent) item)) return false;
+ return intent.getExtras().kindofEquals(((Intent) item).getExtras());
}
@Override
public void describeTo(Description description) {
@@ -115,6 +119,30 @@
return MockitoHamcrest.argThat(m);
}
+ public static Set<String> checkAdminApps(String... adminApps) {
+ final Matcher<Set<String>> m = new BaseMatcher<Set<String>>() {
+ @Override
+ public boolean matches(Object item) {
+ if (item == null) return false;
+ final Set<String> actualAdminApps = (Set<String>) item;
+ if (adminApps.length != actualAdminApps.size()) {
+ return false;
+ }
+ final Set<String> copyOfAdmins = new ArraySet<>(actualAdminApps);
+ for (String adminApp : adminApps) {
+ copyOfAdmins.remove(adminApp);
+ }
+ return copyOfAdmins.isEmpty();
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Admin apps=" + Arrays.toString(adminApps));
+ }
+ };
+ return MockitoHamcrest.argThat(m);
+ }
+
private static String getRestrictionsAsString(Bundle b) {
final StringBuilder sb = new StringBuilder();
sb.append("[");
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index 08edd52..edc7d74 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -447,21 +447,24 @@
@Test
public void testParcelUnParcel() {
Parcel parcel = Parcel.obtain();
- BrightnessChangeEvent event = new BrightnessChangeEvent();
- event.brightness = 23f;
- event.timeStamp = 345L;
- event.packageName = "com.example";
- event.userId = 12;
- event.luxValues = new float[2];
- event.luxValues[0] = 3000.0f;
- event.luxValues[1] = 4000.0f;
- event.luxTimestamps = new long[2];
- event.luxTimestamps[0] = 325L;
- event.luxTimestamps[1] = 315L;
- event.batteryLevel = 0.7f;
- event.nightMode = false;
- event.colorTemperature = 345;
- event.lastBrightness = 50f;
+ BrightnessChangeEvent.Builder builder = new BrightnessChangeEvent.Builder();
+ builder.setBrightness(23f);
+ builder.setTimeStamp(345L);
+ builder.setPackageName("com.example");
+ builder.setUserId(12);
+ float[] luxValues = new float[2];
+ luxValues[0] = 3000.0f;
+ luxValues[1] = 4000.0f;
+ builder.setLuxValues(luxValues);
+ long[] luxTimestamps = new long[2];
+ luxTimestamps[0] = 325L;
+ luxTimestamps[1] = 315L;
+ builder.setLuxTimestamps(luxTimestamps);
+ builder.setBatteryLevel(0.7f);
+ builder.setNightMode(false);
+ builder.setColorTemperature(345);
+ builder.setLastBrightness(50f);
+ BrightnessChangeEvent event = builder.build();
event.writeToParcel(parcel, 0);
byte[] parceled = parcel.marshall();
@@ -485,7 +488,8 @@
assertEquals(event.lastBrightness, event2.lastBrightness, FLOAT_DELTA);
parcel = Parcel.obtain();
- event.batteryLevel = Float.NaN;
+ builder.setBatteryLevel(Float.NaN);
+ event = builder.build();
event.writeToParcel(parcel, 0);
parceled = parcel.marshall();
parcel.recycle();
diff --git a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
index 467b47a..14b118e 100644
--- a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
@@ -37,6 +37,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.Settings;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4;
@@ -50,7 +51,6 @@
import org.junit.runner.RunWith;
/**
- * TODO: Also add a test for temp power whitelist
* Tests that background restrictions on jobs work as expected.
* This test requires test-apps/JobTestApp to be installed on the device.
* To run this test from root of checkout:
@@ -144,15 +144,29 @@
awaitJobStop(DEFAULT_WAIT_TIMEOUT));
}
+ @Test
+ public void testFeatureFlag() throws Exception {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.FORCED_APP_STANDBY_ENABLED, 0);
+ scheduleAndAssertJobStarted();
+ setAppOpsModeAllowed(false);
+ mIActivityManager.makePackageIdle(TEST_APP_PACKAGE, UserHandle.USER_CURRENT);
+ assertFalse("Job stopped even when feature flag was disabled",
+ awaitJobStop(DEFAULT_WAIT_TIMEOUT));
+ }
+
@After
public void tearDown() throws Exception {
- Intent cancelJobsIntent = new Intent(TestJobActivity.ACTION_CANCEL_JOBS);
+ final Intent cancelJobsIntent = new Intent(TestJobActivity.ACTION_CANCEL_JOBS);
cancelJobsIntent.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY));
cancelJobsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(cancelJobsIntent);
mContext.unregisterReceiver(mJobStateChangeReceiver);
+ Thread.sleep(500); // To avoid race with register in the next setUp
setAppOpsModeAllowed(true);
setPowerWhiteListed(false);
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.FORCED_APP_STANDBY_ENABLED, 1);
}
private void setPowerWhiteListed(boolean whitelist) throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index ccf2aaf..272b5d8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -27,6 +27,7 @@
import android.app.IActivityManager;
import android.app.NotificationManager;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManagerInternal;
import android.app.trust.TrustManager;
import android.content.ComponentName;
import android.content.pm.UserInfo;
@@ -41,6 +42,7 @@
import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockPatternUtils;
+import com.android.server.LocalServices;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@@ -75,6 +77,7 @@
FakeStorageManager mStorageManager;
IActivityManager mActivityManager;
DevicePolicyManager mDevicePolicyManager;
+ DevicePolicyManagerInternal mDevicePolicyManagerInternal;
KeyStore mKeyStore;
MockSyntheticPasswordManager mSpManager;
@@ -88,6 +91,10 @@
mStorageManager = new FakeStorageManager();
mActivityManager = mock(IActivityManager.class);
mDevicePolicyManager = mock(DevicePolicyManager.class);
+ mDevicePolicyManagerInternal = mock(DevicePolicyManagerInternal.class);
+
+ LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+ LocalServices.addService(DevicePolicyManagerInternal.class, mDevicePolicyManagerInternal);
mContext = new MockLockSettingsContext(getContext(), mUserManager, mNotificationManager,
mDevicePolicyManager, mock(StorageManager.class), mock(TrustManager.class));
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java
new file mode 100644
index 0000000..4ad9f19
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.locksettings;
+
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.server.testutils.TestUtils.assertExpectException;
+
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.os.RemoteException;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.VerifyCredentialResponse;
+import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
+
+/**
+ * Run the synthetic password tests with caching enabled.
+ *
+ * By default, those tests run without caching. Untrusted credential reset depends on caching so
+ * this class included those tests.
+ */
+public class CachedSyntheticPasswordTests extends SyntheticPasswordTests {
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ enableSpCaching(true);
+ }
+
+ private void enableSpCaching(boolean enable) {
+ when(mDevicePolicyManagerInternal
+ .canUserHaveUntrustedCredentialReset(anyInt())).thenReturn(enable);
+ }
+
+ public void testSyntheticPasswordClearCredentialUntrusted() throws RemoteException {
+ final String PASSWORD = "testSyntheticPasswordClearCredential-password";
+ final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword";
+
+ initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+ long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
+ // clear password
+ mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, null,
+ PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
+ assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
+
+ // set a new password
+ mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+ PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+ NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ .getResponseCode());
+ assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
+ }
+
+ public void testSyntheticPasswordChangeCredentialUntrusted() throws RemoteException {
+ final String PASSWORD = "testSyntheticPasswordClearCredential-password";
+ final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword";
+
+ initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+ long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
+ // Untrusted change password
+ mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+ PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+ assertNotEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
+ assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
+
+ // Verify the password
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+ NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ .getResponseCode());
+ }
+
+ public void testUntrustedCredentialChangeBlockedIfSpNotCached() throws RemoteException {
+ final String PASSWORD = "testUntrustedCredentialChangeBlockedIfSpNotCached-password";
+ final String NEWPASSWORD = "testUntrustedCredentialChangeBlockedIfSpNotCached-newpassword";
+
+ // Disable caching for this test
+ enableSpCaching(false);
+
+ initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+ long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
+ // Untrusted change password
+ assertExpectException(IllegalStateException.class, /* messageRegex= */ null,
+ () -> mService.setLockCredential(
+ NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ null, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID));
+ assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
+
+ // Verify the new password doesn't work but the old one still does
+ assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential(
+ NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ .getResponseCode());
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+ PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ .getResponseCode());
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 2e4c74f..b07d6ac 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -112,7 +112,7 @@
mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
}
- private void initializeCredentialUnderSP(String password, int userId) throws RemoteException {
+ protected void initializeCredentialUnderSP(String password, int userId) throws RemoteException {
enableSyntheticPassword();
int quality = password != null ? PASSWORD_QUALITY_ALPHABETIC
: PASSWORD_QUALITY_UNSPECIFIED;
@@ -129,7 +129,6 @@
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, PASSWORD,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
- mGateKeeperService.clearSecureUserId(PRIMARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
.getResponseCode());
@@ -170,44 +169,6 @@
assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
}
- public void testSyntheticPasswordClearCredentialUntrusted() throws RemoteException {
- final String PASSWORD = "testSyntheticPasswordClearCredential-password";
- final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword";
-
- initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
- long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
- // clear password
- mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, null,
- PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
- assertEquals(0 ,mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
-
- // set a new password
- mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
- .getResponseCode());
- assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
- }
-
- public void testSyntheticPasswordChangeCredentialUntrusted() throws RemoteException {
- final String PASSWORD = "testSyntheticPasswordClearCredential-password";
- final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword";
-
- initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
- long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
- // Untrusted change password
- mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
- assertNotEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
- assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
-
- // Verify the password
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
- .getResponseCode());
- }
-
public void testManagedProfileUnifiedChallengeMigration() throws RemoteException {
final String UnifiedPassword = "testManagedProfileUnifiedChallengeMigration-pwd";
disableSyntheticPassword();
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index 8eaf50a..c6fc675 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -16,9 +16,11 @@
package com.android.server.locksettings.recoverablekeystore;
-import static android.security.recoverablekeystore.KeyStoreRecoveryMetadata.TYPE_PASSWORD;
-import static android.security.recoverablekeystore.KeyStoreRecoveryMetadata.TYPE_PATTERN;
-import static android.security.recoverablekeystore.KeyStoreRecoveryMetadata.TYPE_PIN;
+import static android.security.keystore.KeychainProtectionParams.TYPE_LOCKSCREEN;
+
+import static android.security.keystore.KeychainProtectionParams.TYPE_PASSWORD;
+import static android.security.keystore.KeychainProtectionParams.TYPE_PATTERN;
+import static android.security.keystore.KeychainProtectionParams.TYPE_PIN;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
@@ -38,9 +40,9 @@
import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
-import android.security.recoverablekeystore.KeyDerivationParameters;
-import android.security.recoverablekeystore.KeyEntryRecoveryData;
-import android.security.recoverablekeystore.KeyStoreRecoveryData;
+import android.security.keystore.KeyDerivationParams;
+import android.security.keystore.KeychainSnapshot;
+import android.security.keystore.WrappedApplicationKey;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -75,7 +77,8 @@
private static final int TEST_USER_ID = 1000;
private static final int TEST_RECOVERY_AGENT_UID = 10009;
private static final int TEST_RECOVERY_AGENT_UID2 = 10010;
- private static final long TEST_DEVICE_ID = 13295035643L;
+ private static final byte[] TEST_VAULT_HANDLE =
+ new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14, 15, 16, 17};
private static final String TEST_APP_KEY_ALIAS = "rcleaver";
private static final int TEST_GENERATION_ID = 2;
private static final int TEST_CREDENTIAL_TYPE = CREDENTIAL_TYPE_PASSWORD;
@@ -104,6 +107,10 @@
mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
mKeyPair = SecureBox.genKeyPair();
+ mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
+ new int[] {TYPE_LOCKSCREEN});
+ mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2,
+ new int[] {TYPE_LOCKSCREEN});
mRecoverySnapshotStorage = new RecoverySnapshotStorage();
mKeySyncTask = new KeySyncTask(
@@ -233,8 +240,8 @@
@Test
public void run_doesNotSendAnythingIfNoRecoveryAgentPendingIntentRegistered() throws Exception {
SecretKey applicationKey = generateKey();
- mRecoverableKeyStoreDb.setServerParameters(
- TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_DEVICE_ID);
+ mRecoverableKeyStoreDb.setServerParams(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
mRecoverableKeyStoreDb.insertKey(
TEST_USER_ID,
@@ -269,7 +276,6 @@
@Test
public void run_sendsEncryptedKeysIfAvailableToSync() throws Exception {
-
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
@@ -277,28 +283,28 @@
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
mKeySyncTask.run();
- KeyStoreRecoveryData recoveryData = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
- KeyDerivationParameters keyDerivationParameters =
- recoveryData.getRecoveryMetadata().get(0).getKeyDerivationParameters();
- assertThat(keyDerivationParameters.getAlgorithm()).isEqualTo(
- KeyDerivationParameters.ALGORITHM_SHA256);
+ KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+ KeyDerivationParams KeyDerivationParams =
+ keychainSnapshot.getKeychainProtectionParams().get(0).getKeyDerivationParams();
+ assertThat(KeyDerivationParams.getAlgorithm()).isEqualTo(
+ KeyDerivationParams.ALGORITHM_SHA256);
verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
byte[] lockScreenHash = KeySyncTask.hashCredentials(
- keyDerivationParameters.getSalt(),
+ KeyDerivationParams.getSalt(),
TEST_CREDENTIAL);
Long counterId = mRecoverableKeyStoreDb.getCounterId(TEST_USER_ID, TEST_RECOVERY_AGENT_UID);
assertThat(counterId).isNotNull();
byte[] recoveryKey = decryptThmEncryptedKey(
lockScreenHash,
- recoveryData.getEncryptedRecoveryKeyBlob(),
+ keychainSnapshot.getEncryptedRecoveryKeyBlob(),
/*vaultParams=*/ KeySyncUtils.packVaultParams(
mKeyPair.getPublic(),
counterId,
/*maxAttempts=*/ 10,
- TEST_DEVICE_ID));
- List<KeyEntryRecoveryData> applicationKeys = recoveryData.getApplicationKeyBlobs();
+ TEST_VAULT_HANDLE));
+ List<WrappedApplicationKey> applicationKeys = keychainSnapshot.getWrappedApplicationKeys();
assertThat(applicationKeys).hasSize(1);
- KeyEntryRecoveryData keyData = applicationKeys.get(0);
+ WrappedApplicationKey keyData = applicationKeys.get(0);
assertEquals(TEST_APP_KEY_ALIAS, keyData.getAlias());
assertThat(keyData.getAlias()).isEqualTo(keyData.getAlias());
byte[] appKey = KeySyncUtils.decryptApplicationKey(
@@ -308,7 +314,6 @@
@Test
public void run_setsCorrectSnapshotVersion() throws Exception {
-
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
@@ -317,14 +322,14 @@
mKeySyncTask.run();
- KeyStoreRecoveryData recoveryData = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
- assertThat(recoveryData.getSnapshotVersion()).isEqualTo(1); // default value;
+ KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+ assertThat(keychainSnapshot.getSnapshotVersion()).isEqualTo(1); // default value;
mRecoverableKeyStoreDb.setShouldCreateSnapshot(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, true);
mKeySyncTask.run();
- recoveryData = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
- assertThat(recoveryData.getSnapshotVersion()).isEqualTo(2); // Updated
+ keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+ assertThat(keychainSnapshot.getSnapshotVersion()).isEqualTo(2); // Updated
}
@Test
@@ -347,9 +352,9 @@
mKeySyncTask.run();
- KeyStoreRecoveryData recoveryData = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
- assertThat(recoveryData.getRecoveryMetadata()).hasSize(1);
- assertThat(recoveryData.getRecoveryMetadata().get(1).getLockScreenUiFormat()).
+ KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+ assertThat(keychainSnapshot.getKeychainProtectionParams()).hasSize(1);
+ assertThat(keychainSnapshot.getKeychainProtectionParams().get(0).getLockScreenUiFormat()).
isEqualTo(TYPE_PASSWORD);
}
@@ -373,10 +378,10 @@
mKeySyncTask.run();
- KeyStoreRecoveryData recoveryData = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
- assertThat(recoveryData.getRecoveryMetadata()).hasSize(1);
+ KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+ assertThat(keychainSnapshot.getKeychainProtectionParams()).hasSize(1);
// Password with only digits is changed to pin.
- assertThat(recoveryData.getRecoveryMetadata().get(1).getLockScreenUiFormat()).
+ assertThat(keychainSnapshot.getKeychainProtectionParams().get(0).getLockScreenUiFormat()).
isEqualTo(TYPE_PIN);
}
@@ -400,16 +405,14 @@
mKeySyncTask.run();
- KeyStoreRecoveryData recoveryData = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
- assertThat(recoveryData.getRecoveryMetadata()).hasSize(1);
- assertThat(recoveryData.getRecoveryMetadata().get(1).getLockScreenUiFormat()).
+ KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+ assertThat(keychainSnapshot.getKeychainProtectionParams()).hasSize(1);
+ assertThat(keychainSnapshot.getKeychainProtectionParams().get(0).getLockScreenUiFormat()).
isEqualTo(TYPE_PATTERN);
}
-
@Test
public void run_sendsEncryptedKeysWithTwoRegisteredAgents() throws Exception {
-
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
@@ -425,13 +428,35 @@
}
@Test
- public void run_doesNotSendKeyToNonregisteredAgent() throws Exception {
+ public void run_sendsEncryptedKeysOnlyForAgentWhichActiveUserSecretType() throws Exception {
+ mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
+ new int[] {TYPE_LOCKSCREEN, 1000});
+ // Snapshot will not be created during unlock event.
+ mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2,
+ new int[] {1000});
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, mKeyPair.getPublic());
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
+ when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(true);
+ addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+ addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_APP_KEY_ALIAS);
+ mKeySyncTask.run();
+
+ verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
+ verify(mSnapshotListenersStorage, never()).
+ recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID2);
+ }
+
+ @Test
+ public void run_doesNotSendKeyToNonregisteredAgent() throws Exception {
+ mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
+ mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, mKeyPair.getPublic());
+ when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(false);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_APP_KEY_ALIAS);
@@ -445,8 +470,8 @@
private SecretKey addApplicationKey(int userId, int recoveryAgentUid, String alias)
throws Exception{
SecretKey applicationKey = generateKey();
- mRecoverableKeyStoreDb.setServerParameters(
- userId, recoveryAgentUid, TEST_DEVICE_ID);
+ mRecoverableKeyStoreDb.setServerParams(
+ userId, recoveryAgentUid, TEST_VAULT_HANDLE);
mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, TEST_GENERATION_ID);
// Newly added key is not synced.
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
index 114da1a..ba6b274 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
@@ -50,6 +50,8 @@
private static final int RECOVERY_KEY_LENGTH_BITS = 256;
private static final int THM_KF_HASH_SIZE = 256;
private static final int KEY_CLAIMANT_LENGTH_BYTES = 16;
+ private static final byte[] TEST_VAULT_HANDLE =
+ new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14, 15, 16, 17};
private static final String SHA_256_ALGORITHM = "SHA-256";
private static final String APPLICATION_KEY_ALGORITHM = "AES";
private static final byte[] LOCK_SCREEN_HASH_1 =
@@ -61,7 +63,8 @@
private static final byte[] RECOVERY_RESPONSE_HEADER =
"V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
private static final int PUBLIC_KEY_LENGTH_BYTES = 65;
- private static final int VAULT_PARAMS_LENGTH_BYTES = 85;
+ private static final int VAULT_PARAMS_LENGTH_BYTES = 94;
+ private static final int VAULT_HANDLE_LENGTH_BYTES = 17;
@Test
public void calculateThmKfHash_isShaOfLockScreenHashWithPrefix() throws Exception {
@@ -342,14 +345,14 @@
}
@Test
- public void packVaultParams_returns85Bytes() throws Exception {
+ public void packVaultParams_returns94Bytes() throws Exception {
PublicKey thmPublicKey = SecureBox.genKeyPair().getPublic();
byte[] packedForm = KeySyncUtils.packVaultParams(
thmPublicKey,
/*counterId=*/ 1001L,
/*maxAttempts=*/ 10,
- /*deviceId=*/ 1L);
+ TEST_VAULT_HANDLE);
assertEquals(VAULT_PARAMS_LENGTH_BYTES, packedForm.length);
}
@@ -362,7 +365,7 @@
thmPublicKey,
/*counterId=*/ 1001L,
/*maxAttempts=*/ 10,
- /*deviceId=*/ 1L);
+ TEST_VAULT_HANDLE);
assertArrayEquals(
SecureBox.encodePublicKey(thmPublicKey),
@@ -377,7 +380,7 @@
SecureBox.genKeyPair().getPublic(),
counterId,
/*maxAttempts=*/ 10,
- /*deviceId=*/ 1L);
+ TEST_VAULT_HANDLE);
ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm)
.order(ByteOrder.LITTLE_ENDIAN);
@@ -386,37 +389,37 @@
}
@Test
- public void packVaultParams_encodesDeviceIdAsThirdParam() throws Exception {
- long deviceId = 102942158152L;
-
- byte[] packedForm = KeySyncUtils.packVaultParams(
- SecureBox.genKeyPair().getPublic(),
- /*counterId=*/ 10021L,
- /*maxAttempts=*/ 10,
- deviceId);
-
- ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm)
- .order(ByteOrder.LITTLE_ENDIAN);
- byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES);
- assertEquals(deviceId, byteBuffer.getLong());
- }
-
- @Test
- public void packVaultParams_encodesMaxAttemptsAsLastParam() throws Exception {
+ public void packVaultParams_encodesMaxAttemptsAsThirdParam() throws Exception {
int maxAttempts = 10;
byte[] packedForm = KeySyncUtils.packVaultParams(
SecureBox.genKeyPair().getPublic(),
/*counterId=*/ 1001L,
maxAttempts,
- /*deviceId=*/ 1L);
+ TEST_VAULT_HANDLE);
ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm)
.order(ByteOrder.LITTLE_ENDIAN);
- byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + 2 * Long.BYTES);
+ byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES);
assertEquals(maxAttempts, byteBuffer.getInt());
}
+ @Test
+ public void packVaultParams_encodesVaultHandleAsLastParam() throws Exception {
+ byte[] packedForm = KeySyncUtils.packVaultParams(
+ SecureBox.genKeyPair().getPublic(),
+ /*counterId=*/ 10021L,
+ /*maxAttempts=*/ 10,
+ TEST_VAULT_HANDLE);
+
+ ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm)
+ .order(ByteOrder.LITTLE_ENDIAN);
+ byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES + Integer.BYTES);
+ byte[] vaultHandle = new byte[VAULT_HANDLE_LENGTH_BYTES];
+ byteBuffer.get(vaultHandle);
+ // TODO: Fix this once we fix the code in the KeySyncUtils class
+ assertArrayEquals(new byte[VAULT_HANDLE_LENGTH_BYTES], vaultHandle);
+ }
private static byte[] randomBytes(int n) {
byte[] bytes = new byte[n];
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index ac2d36b..767472a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -16,8 +16,8 @@
package com.android.server.locksettings.recoverablekeystore;
-import static android.security.recoverablekeystore.KeyStoreRecoveryMetadata.TYPE_LOCKSCREEN;
-import static android.security.recoverablekeystore.KeyStoreRecoveryMetadata.TYPE_PASSWORD;
+import static android.security.keystore.KeychainProtectionParams.TYPE_LOCKSCREEN;
+import static android.security.keystore.KeychainProtectionParams.TYPE_PASSWORD;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertArrayEquals;
@@ -35,18 +35,18 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.Manifest;
import android.os.Binder;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
-import android.security.recoverablekeystore.KeyDerivationParameters;
-import android.security.recoverablekeystore.KeyEntryRecoveryData;
-import android.security.recoverablekeystore.KeyStoreRecoveryMetadata;
-import android.security.recoverablekeystore.RecoverableKeyStoreLoader;
-import android.support.test.InstrumentationRegistry;
+import android.security.keystore.KeyDerivationParams;
+import android.security.keystore.KeychainProtectionParams;
+import android.security.keystore.WrappedApplicationKey;
import android.support.test.filters.SmallTest;
+import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
@@ -78,6 +78,7 @@
public class RecoverableKeyStoreManagerTest {
private static final String DATABASE_FILE_NAME = "recoverablekeystore.db";
+ private static final String ROOT_CERTIFICATE_ALIAS = "put_default_alias_here";
private static final String TEST_SESSION_ID = "karlin";
private static final byte[] TEST_PUBLIC_KEY = new byte[] {
(byte) 0x30, (byte) 0x59, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x07, (byte) 0x2a,
@@ -206,10 +207,9 @@
}
@Test
- public void removeKey_UpdatesShouldCreateSnapshot() throws Exception {
+ public void removeKey_updatesShouldCreateSnapshot() throws Exception {
int uid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
-
mRecoverableKeyStoreManager.generateAndStoreKey(TEST_ALIAS);
// Pretend that key was synced
mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
@@ -220,6 +220,29 @@
}
@Test
+ public void removeKey_failureDoesNotUpdateShouldCreateSnapshot() throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
+ // Key did not exist
+ mRecoverableKeyStoreManager.removeKey(TEST_ALIAS);
+
+ assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
+ }
+
+ @Test
+ public void initRecoveryService_updatesShouldCreateSnapshot() throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ // Sync is not needed.
+ mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
+
+ mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, TEST_PUBLIC_KEY);
+
+ assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+ }
+
+ @Test
public void startRecoverySession_checksPermissionFirst() throws Exception {
mRecoverableKeyStoreManager.startRecoverySession(
TEST_SESSION_ID,
@@ -227,15 +250,15 @@
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
ImmutableList.of(
- new KeyStoreRecoveryMetadata(
+ new KeychainProtectionParams(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
- KeyDerivationParameters.createSha256Parameters(TEST_SALT),
+ KeyDerivationParams.createSha256Params(TEST_SALT),
TEST_SECRET)));
verify(mMockContext, times(1))
.enforceCallingOrSelfPermission(
- eq(RecoverableKeyStoreLoader.PERMISSION_RECOVER_KEYSTORE), any());
+ eq(Manifest.permission.RECOVER_KEYSTORE), any());
}
@Test
@@ -246,10 +269,10 @@
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
ImmutableList.of(
- new KeyStoreRecoveryMetadata(
+ new KeychainProtectionParams(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
- KeyDerivationParameters.createSha256Parameters(TEST_SALT),
+ KeyDerivationParams.createSha256Params(TEST_SALT),
TEST_SECRET)));
assertEquals(1, mRecoverySessionStorage.size());
@@ -271,7 +294,7 @@
fail("should have thrown");
} catch (ServiceSpecificException e) {
assertThat(e.getMessage()).startsWith(
- "Only a single KeyStoreRecoveryMetadata is supported");
+ "Only a single KeychainProtectionParams is supported");
}
}
@@ -284,10 +307,10 @@
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
ImmutableList.of(
- new KeyStoreRecoveryMetadata(
+ new KeychainProtectionParams(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
- KeyDerivationParameters.createSha256Parameters(TEST_SALT),
+ KeyDerivationParams.createSha256Params(TEST_SALT),
TEST_SECRET)));
fail("should have thrown");
} catch (ServiceSpecificException e) {
@@ -306,10 +329,10 @@
vaultParams,
TEST_VAULT_CHALLENGE,
ImmutableList.of(
- new KeyStoreRecoveryMetadata(
+ new KeychainProtectionParams(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
- KeyDerivationParameters.createSha256Parameters(TEST_SALT),
+ KeyDerivationParams.createSha256Params(TEST_SALT),
TEST_SECRET)));
fail("should have thrown");
} catch (ServiceSpecificException e) {
@@ -324,7 +347,7 @@
TEST_SESSION_ID,
/*recoveryKeyBlob=*/ randomBytes(32),
/*applicationKeys=*/ ImmutableList.of(
- new KeyEntryRecoveryData("alias", randomBytes(32))
+ new WrappedApplicationKey("alias", randomBytes(32))
));
fail("should have thrown");
} catch (ServiceSpecificException e) {
@@ -339,10 +362,10 @@
TEST_PUBLIC_KEY,
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(new KeyStoreRecoveryMetadata(
+ ImmutableList.of(new KeychainProtectionParams(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
- KeyDerivationParameters.createSha256Parameters(TEST_SALT),
+ KeyDerivationParams.createSha256Params(TEST_SALT),
TEST_SECRET)));
try {
@@ -363,17 +386,17 @@
TEST_PUBLIC_KEY,
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(new KeyStoreRecoveryMetadata(
+ ImmutableList.of(new KeychainProtectionParams(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
- KeyDerivationParameters.createSha256Parameters(TEST_SALT),
+ KeyDerivationParams.createSha256Params(TEST_SALT),
TEST_SECRET)));
byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID)
.getKeyClaimant();
SecretKey recoveryKey = randomRecoveryKey();
byte[] encryptedClaimResponse = encryptClaimResponse(
keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
- KeyEntryRecoveryData badApplicationKey = new KeyEntryRecoveryData(
+ WrappedApplicationKey badApplicationKey = new WrappedApplicationKey(
TEST_ALIAS,
randomBytes(32));
@@ -395,10 +418,10 @@
TEST_PUBLIC_KEY,
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(new KeyStoreRecoveryMetadata(
+ ImmutableList.of(new KeychainProtectionParams(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
- KeyDerivationParameters.createSha256Parameters(TEST_SALT),
+ KeyDerivationParams.createSha256Params(TEST_SALT),
TEST_SECRET)));
byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID)
.getKeyClaimant();
@@ -406,7 +429,7 @@
byte[] encryptedClaimResponse = encryptClaimResponse(
keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
byte[] applicationKeyBytes = randomBytes(32);
- KeyEntryRecoveryData applicationKey = new KeyEntryRecoveryData(
+ WrappedApplicationKey applicationKey = new WrappedApplicationKey(
TEST_ALIAS,
encryptedApplicationKey(recoveryKey, applicationKeyBytes));
@@ -449,6 +472,20 @@
}
@Test
+ public void setRecoverySecretTypes_updatesShouldCreateSnapshot() throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ int[] types = new int[]{1, 2, 3};
+
+ mRecoverableKeyStoreManager.generateAndStoreKey(TEST_ALIAS);
+ // Pretend that key was synced
+ mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
+ mRecoverableKeyStoreManager.setRecoverySecretTypes(types);
+
+ assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+ }
+
+ @Test
public void setRecoveryStatus_forOneAlias() throws Exception {
int userId = UserHandle.getCallingUserId();
int uid = Binder.getCallingUid();
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
index b8080ab..5cb7b67 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
@@ -29,7 +29,7 @@
import android.content.Context;
import android.content.SharedPreferences;
-import android.security.recoverablekeystore.RecoverableKeyStoreLoader;
+import android.security.keystore.RecoveryManager;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -51,6 +51,12 @@
private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
private File mDatabaseFile;
+ private static final byte[] SERVER_PARAMS =
+ new byte[]{1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2};
+
+ private static final byte[] SERVER_PARAMS2 =
+ new byte[]{1, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4};
+
@Before
public void setUp() {
Context context = InstrumentationRegistry.getTargetContext();
@@ -277,8 +283,7 @@
Map<String, Integer> statuses = mRecoverableKeyStoreDb.getStatusForAllKeys(uid);
assertThat(statuses).hasSize(3);
- assertThat(statuses).containsEntry(alias,
- RecoverableKeyStoreLoader.RECOVERY_STATUS_SYNC_IN_PROGRESS);
+ assertThat(statuses).containsEntry(alias, RecoveryManager.RECOVERY_STATUS_SYNC_IN_PROGRESS);
assertThat(statuses).containsEntry(alias2, status);
assertThat(statuses).containsEntry(alias3, status);
@@ -329,8 +334,7 @@
int uid = 10009;
assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull();
- long serverParams = 123456L;
- mRecoverableKeyStoreDb.setServerParameters(userId, uid, serverParams);
+ mRecoverableKeyStoreDb.setServerParams(userId, uid, SERVER_PARAMS);
assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull();
}
@@ -438,32 +442,30 @@
PublicKey pubkey1 = genRandomPublicKey();
int[] types1 = new int[]{1};
- long serverParams1 = 111L;
PublicKey pubkey2 = genRandomPublicKey();
int[] types2 = new int[]{2};
- long serverParams2 = 222L;
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid, pubkey1);
mRecoverableKeyStoreDb.setRecoverySecretTypes(userId, uid, types1);
- mRecoverableKeyStoreDb.setServerParameters(userId, uid, serverParams1);
+ mRecoverableKeyStoreDb.setServerParams(userId, uid, SERVER_PARAMS);
assertThat(mRecoverableKeyStoreDb.getRecoverySecretTypes(userId, uid)).isEqualTo(
types1);
- assertThat(mRecoverableKeyStoreDb.getServerParameters(userId, uid)).isEqualTo(
- serverParams1);
+ assertThat(mRecoverableKeyStoreDb.getServerParams(userId, uid)).isEqualTo(
+ SERVER_PARAMS);
assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isEqualTo(
pubkey1);
// Check that the methods don't interfere with each other.
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid, pubkey2);
mRecoverableKeyStoreDb.setRecoverySecretTypes(userId, uid, types2);
- mRecoverableKeyStoreDb.setServerParameters(userId, uid, serverParams2);
+ mRecoverableKeyStoreDb.setServerParams(userId, uid, SERVER_PARAMS2);
assertThat(mRecoverableKeyStoreDb.getRecoverySecretTypes(userId, uid)).isEqualTo(
types2);
- assertThat(mRecoverableKeyStoreDb.getServerParameters(userId, uid)).isEqualTo(
- serverParams2);
+ assertThat(mRecoverableKeyStoreDb.getServerParams(userId, uid)).isEqualTo(
+ SERVER_PARAMS2);
assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isEqualTo(
pubkey2);
}
@@ -480,35 +482,33 @@
}
@Test
- public void setServerParameters_replaceOldValue() throws Exception {
+ public void setServerParams_replaceOldValue() throws Exception {
int userId = 12;
int uid = 10009;
- long serverParams1 = 111L;
- long serverParams2 = 222L;
- mRecoverableKeyStoreDb.setServerParameters(userId, uid, serverParams1);
- mRecoverableKeyStoreDb.setServerParameters(userId, uid, serverParams2);
- assertThat(mRecoverableKeyStoreDb.getServerParameters(userId, uid)).isEqualTo(
- serverParams2);
+
+ mRecoverableKeyStoreDb.setServerParams(userId, uid, SERVER_PARAMS);
+ mRecoverableKeyStoreDb.setServerParams(userId, uid, SERVER_PARAMS2);
+ assertThat(mRecoverableKeyStoreDb.getServerParams(userId, uid)).isEqualTo(
+ SERVER_PARAMS2);
}
@Test
- public void getServerParameters_returnsNullIfNoValue() throws Exception {
+ public void getServerParams_returnsNullIfNoValue() throws Exception {
int userId = 12;
int uid = 10009;
- assertThat(mRecoverableKeyStoreDb.getServerParameters(userId, uid)).isNull();
+ assertThat(mRecoverableKeyStoreDb.getServerParams(userId, uid)).isNull();
PublicKey pubkey = genRandomPublicKey();
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid, pubkey);
- assertThat(mRecoverableKeyStoreDb.getServerParameters(userId, uid)).isNull();
+ assertThat(mRecoverableKeyStoreDb.getServerParams(userId, uid)).isNull();
}
@Test
- public void getServerParameters_returnsInsertedValue() throws Exception {
+ public void getServerParams_returnsInsertedValue() throws Exception {
int userId = 12;
int uid = 10009;
- long serverParams = 123456L;
- mRecoverableKeyStoreDb.setServerParameters(userId, uid, serverParams);
- assertThat(mRecoverableKeyStoreDb.getServerParameters(userId, uid)).isEqualTo(serverParams);
+ mRecoverableKeyStoreDb.setServerParams(userId, uid, SERVER_PARAMS);
+ assertThat(mRecoverableKeyStoreDb.getServerParams(userId, uid)).isEqualTo(SERVER_PARAMS);
}
@Test
@@ -591,22 +591,21 @@
int uid = 10009;
PublicKey pubkey1 = genRandomPublicKey();
PublicKey pubkey2 = genRandomPublicKey();
- long serverParams = 123456L;
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid, pubkey1);
assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isEqualTo(
pubkey1);
- assertThat(mRecoverableKeyStoreDb.getServerParameters(userId, uid)).isNull();
+ assertThat(mRecoverableKeyStoreDb.getServerParams(userId, uid)).isNull();
- mRecoverableKeyStoreDb.setServerParameters(userId, uid, serverParams);
+ mRecoverableKeyStoreDb.setServerParams(userId, uid, SERVER_PARAMS);
assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isEqualTo(
pubkey1);
- assertThat(mRecoverableKeyStoreDb.getServerParameters(userId, uid)).isEqualTo(serverParams);
+ assertThat(mRecoverableKeyStoreDb.getServerParams(userId, uid)).isEqualTo(SERVER_PARAMS);
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid, pubkey2);
assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isEqualTo(
pubkey2);
- assertThat(mRecoverableKeyStoreDb.getServerParameters(userId, uid)).isEqualTo(serverParams);
+ assertThat(mRecoverableKeyStoreDb.getServerParams(userId, uid)).isEqualTo(SERVER_PARAMS);
}
private static byte[] getUtf8Bytes(String s) {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
index 2759e39..56b44e2 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
@@ -3,7 +3,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
-import android.security.recoverablekeystore.KeyStoreRecoveryData;
+import android.security.keystore.KeychainSnapshot;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -26,25 +26,25 @@
@Test
public void get_returnsSetSnapshot() {
int userId = 1000;
- KeyStoreRecoveryData recoveryData = new KeyStoreRecoveryData(
+ KeychainSnapshot keychainSnapshot = new KeychainSnapshot(
/*snapshotVersion=*/ 1,
new ArrayList<>(),
new ArrayList<>(),
new byte[0]);
- mRecoverySnapshotStorage.put(userId, recoveryData);
+ mRecoverySnapshotStorage.put(userId, keychainSnapshot);
- assertEquals(recoveryData, mRecoverySnapshotStorage.get(userId));
+ assertEquals(keychainSnapshot, mRecoverySnapshotStorage.get(userId));
}
@Test
public void remove_removesSnapshots() {
int userId = 1000;
- KeyStoreRecoveryData recoveryData = new KeyStoreRecoveryData(
+ KeychainSnapshot keychainSnapshot = new KeychainSnapshot(
/*snapshotVersion=*/ 1,
new ArrayList<>(),
new ArrayList<>(),
new byte[0]);
- mRecoverySnapshotStorage.put(userId, recoveryData);
+ mRecoverySnapshotStorage.put(userId, keychainSnapshot);
mRecoverySnapshotStorage.remove(userId);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index b073ee5..0abb48f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -294,6 +294,7 @@
null /*disabledPkg*/,
null /*sharedUser*/,
UPDATED_CODE_PATH /*codePath*/,
+ null /*resourcePath*/,
null /*legacyNativeLibraryPath*/,
"arm64-v8a" /*primaryCpuAbi*/,
"armeabi" /*secondaryCpuAbi*/,
@@ -327,6 +328,7 @@
null /*disabledPkg*/,
null /*sharedUser*/,
UPDATED_CODE_PATH /*codePath*/,
+ null /*resourcePath*/,
null /*legacyNativeLibraryPath*/,
"arm64-v8a" /*primaryCpuAbi*/,
"armeabi" /*secondaryCpuAbi*/,
@@ -367,6 +369,7 @@
null /*disabledPkg*/,
testUserSetting01 /*sharedUser*/,
UPDATED_CODE_PATH /*codePath*/,
+ null /*resourcePath*/,
null /*legacyNativeLibraryPath*/,
"arm64-v8a" /*primaryCpuAbi*/,
"armeabi" /*secondaryCpuAbi*/,
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 32b0b26..49601c3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -34,7 +34,6 @@
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
-import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -262,14 +261,13 @@
assertBundleApproximateEquals(a.mAppMetaData, b.mAppMetaData);
assertEquals(a.mVersionName, b.mVersionName);
assertEquals(a.mSharedUserId, b.mSharedUserId);
- assertTrue(Arrays.equals(a.mSignatures, b.mSignatures));
- assertTrue(Arrays.equals(a.mCertificates, b.mCertificates));
+ assertTrue(Arrays.equals(a.mSigningDetails.signatures, b.mSigningDetails.signatures));
assertTrue(Arrays.equals(a.mLastPackageUsageTimeInMills, b.mLastPackageUsageTimeInMills));
assertEquals(a.mExtras, b.mExtras);
assertEquals(a.mRestrictedAccountType, b.mRestrictedAccountType);
assertEquals(a.mRequiredAccountType, b.mRequiredAccountType);
assertEquals(a.mOverlayTarget, b.mOverlayTarget);
- assertEquals(a.mSigningKeys, b.mSigningKeys);
+ assertEquals(a.mSigningDetails.publicKeys, b.mSigningDetails.publicKeys);
assertEquals(a.mUpgradeKeySets, b.mUpgradeKeySets);
assertEquals(a.mKeySetMapping, b.mKeySetMapping);
assertEquals(a.cpuAbiOverride, b.cpuAbiOverride);
@@ -495,14 +493,16 @@
pkg.mAppMetaData = new Bundle();
pkg.mVersionName = "foo17";
pkg.mSharedUserId = "foo18";
- pkg.mSignatures = new Signature[] { new Signature(new byte[16]) };
- pkg.mCertificates = new Certificate[][] { new Certificate[] { null }};
+ pkg.mSigningDetails =
+ new PackageParser.SigningDetails(
+ new Signature[] { new Signature(new byte[16]) },
+ 2,
+ new ArraySet<>());
pkg.mExtras = new Bundle();
pkg.mRestrictedAccountType = "foo19";
pkg.mRequiredAccountType = "foo20";
pkg.mOverlayTarget = "foo21";
pkg.mOverlayPriority = 100;
- pkg.mSigningKeys = new ArraySet<>();
pkg.mUpgradeKeySets = new ArraySet<>();
pkg.mKeySetMapping = new ArrayMap<>();
pkg.cpuAbiOverride = "foo22";
diff --git a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
index d1e0132..a628b7b 100644
--- a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
+++ b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.graphics.Point;
import android.graphics.Rect;
+import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.IApplicationToken;
@@ -43,6 +44,7 @@
public boolean inMultiWindowMode;
public boolean visible = true;
public int surfaceLayer = 1;
+ public boolean isDimming = false;
public boolean policyVisible = true;
@@ -221,7 +223,7 @@
@Override
public boolean isDimming() {
- throw new UnsupportedOperationException("not implemented");
+ return isDimming;
}
@Override
@@ -253,4 +255,9 @@
public boolean canAcquireSleepToken() {
throw new UnsupportedOperationException("not implemented");
}
+
+ @Override
+ public void writeIdentifierToProto(ProtoOutputStream proto, long fieldId){
+ throw new UnsupportedOperationException("not implemented");
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
index 9a6da0e..b6c370e 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
@@ -16,15 +16,16 @@
package com.android.server.policy;
-import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
+import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.WindowManager.LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -34,7 +35,6 @@
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import android.view.Surface;
import android.view.WindowManager;
import org.junit.Before;
@@ -128,7 +128,23 @@
}
@Test
- public void layoutWindowLw_withDisplayCutout_fullscreen() {
+ public void layoutWindowLw_withhDisplayCutout_never() {
+ addDisplayCutout();
+
+ mAppWindow.attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+ mPolicy.addWindow(mAppWindow);
+
+ mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+
+ assertInsetByTopBottom(mAppWindow.parentFrame, STATUS_BAR_HEIGHT, 0);
+ assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
+ }
+
+ @Test
+ public void layoutWindowLw_withDisplayCutout_layoutFullscreen() {
addDisplayCutout();
mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
@@ -137,6 +153,22 @@
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+ assertInsetByTopBottom(mAppWindow.parentFrame, 0, 0);
+ assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
+ }
+
+ @Test
+ public void layoutWindowLw_withDisplayCutout_fullscreen() {
+ addDisplayCutout();
+
+ mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
+ mPolicy.addWindow(mAppWindow);
+
+ mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+
assertInsetByTopBottom(mAppWindow.parentFrame, STATUS_BAR_HEIGHT, 0);
assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
@@ -147,8 +179,8 @@
public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() {
addDisplayCutout();
- mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- mAppWindow.attrs.flags2 |= FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA;
+ mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
+ mAppWindow.attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mPolicy.addWindow(mAppWindow);
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -217,7 +249,7 @@
setRotation(ROTATION_90);
mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- mAppWindow.attrs.flags2 |= FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA;
+ mAppWindow.attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mPolicy.addWindow(mAppWindow);
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java
new file mode 100644
index 0000000..64637f4
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.policy;
+
+import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+
+import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
+import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import android.graphics.PixelFormat;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.WindowManager;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class PhoneWindowManagerTest {
+
+ private static FakeWindowState createOpaqueFullscreen(boolean hasLightNavBar) {
+ final FakeWindowState state = new FakeWindowState();
+ state.attrs = new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT,
+ TYPE_BASE_APPLICATION,
+ FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
+ PixelFormat.OPAQUE);
+ state.attrs.subtreeSystemUiVisibility =
+ hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0;
+ return state;
+ }
+
+ private static FakeWindowState createDimmingDialogWindow(boolean canBeImTarget) {
+ final FakeWindowState state = new FakeWindowState();
+ state.attrs = new WindowManager.LayoutParams(WRAP_CONTENT, WRAP_CONTENT,
+ TYPE_APPLICATION,
+ FLAG_DIM_BEHIND | (canBeImTarget ? 0 : FLAG_ALT_FOCUSABLE_IM),
+ PixelFormat.TRANSLUCENT);
+ state.isDimming = true;
+ return state;
+ }
+
+ private static FakeWindowState createInputMethodWindow(boolean visible, boolean drawNavBar,
+ boolean hasLightNavBar) {
+ final FakeWindowState state = new FakeWindowState();
+ state.attrs = new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT,
+ TYPE_INPUT_METHOD,
+ FLAG_NOT_FOCUSABLE | FLAG_LAYOUT_IN_SCREEN
+ | (drawNavBar ? FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS : 0),
+ PixelFormat.TRANSPARENT);
+ state.attrs.subtreeSystemUiVisibility =
+ hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0;
+ state.visible = visible;
+ state.policyVisible = visible;
+ return state;
+ }
+
+
+ @Test
+ public void testChooseNavigationColorWindowLw() throws Exception {
+ final FakeWindowState opaque = createOpaqueFullscreen(false);
+
+ final FakeWindowState dimmingImTarget = createDimmingDialogWindow(true);
+ final FakeWindowState dimmingNonImTarget = createDimmingDialogWindow(false);
+
+ final FakeWindowState visibleIme = createInputMethodWindow(true, true, false);
+ final FakeWindowState invisibleIme = createInputMethodWindow(false, true, false);
+ final FakeWindowState imeNonDrawNavBar = createInputMethodWindow(true, false, false);
+
+ // If everything is null, return null
+ assertNull(null, PhoneWindowManager.chooseNavigationColorWindowLw(
+ null, null, null, NAV_BAR_BOTTOM));
+
+ assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
+ opaque, opaque, null, NAV_BAR_BOTTOM));
+ assertEquals(dimmingImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
+ opaque, dimmingImTarget, null, NAV_BAR_BOTTOM));
+ assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
+ opaque, dimmingNonImTarget, null, NAV_BAR_BOTTOM));
+
+ assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw(
+ null, null, visibleIme, NAV_BAR_BOTTOM));
+ assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw(
+ null, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
+ assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
+ null, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
+ assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw(
+ opaque, opaque, visibleIme, NAV_BAR_BOTTOM));
+ assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw(
+ opaque, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
+ assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
+ opaque, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
+
+ assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
+ opaque, opaque, invisibleIme, NAV_BAR_BOTTOM));
+ assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
+ opaque, opaque, invisibleIme, NAV_BAR_BOTTOM));
+ assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
+ opaque, opaque, visibleIme, NAV_BAR_RIGHT));
+
+ // Only IME windows that have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS should be navigation color
+ // window.
+ assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
+ opaque, opaque, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+ assertEquals(dimmingImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
+ opaque, dimmingImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+ assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
+ opaque, dimmingNonImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+ }
+
+ @Test
+ public void testUpdateLightNavigationBarLw() throws Exception {
+ final FakeWindowState opaqueDarkNavBar = createOpaqueFullscreen(false);
+ final FakeWindowState opaqueLightNavBar = createOpaqueFullscreen(true);
+
+ final FakeWindowState dimming = createDimmingDialogWindow(false);
+
+ final FakeWindowState imeDrawDarkNavBar = createInputMethodWindow(true,true, false);
+ final FakeWindowState imeDrawLightNavBar = createInputMethodWindow(true,true, true);
+
+ assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+ PhoneWindowManager.updateLightNavigationBarLw(
+ SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null,
+ null, null));
+
+ // Opaque top fullscreen window overrides SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR flag.
+ assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
+ 0, opaqueDarkNavBar, opaqueDarkNavBar, null, opaqueDarkNavBar));
+ assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
+ SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, opaqueDarkNavBar, null,
+ opaqueDarkNavBar));
+ assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+ PhoneWindowManager.updateLightNavigationBarLw(0, opaqueLightNavBar,
+ opaqueLightNavBar, null, opaqueLightNavBar));
+ assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+ PhoneWindowManager.updateLightNavigationBarLw(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+ opaqueLightNavBar, opaqueLightNavBar, null, opaqueLightNavBar));
+
+ // Dimming window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.
+ assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
+ 0, opaqueDarkNavBar, dimming, null, dimming));
+ assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
+ 0, opaqueLightNavBar, dimming, null, dimming));
+ assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
+ SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, dimming, null, dimming));
+ assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
+ SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, null, dimming));
+ assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
+ SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, imeDrawLightNavBar,
+ dimming));
+
+ // IME window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
+ assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
+ SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null, imeDrawDarkNavBar,
+ imeDrawDarkNavBar));
+
+ // Even if the top fullscreen has SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, IME window wins.
+ assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
+ SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, opaqueLightNavBar,
+ imeDrawDarkNavBar, imeDrawDarkNavBar));
+
+ // IME window should be able to use SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.
+ assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+ PhoneWindowManager.updateLightNavigationBarLw(0, opaqueDarkNavBar,
+ opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
index e7e9abad..ad89953 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
@@ -24,6 +24,8 @@
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
@@ -31,6 +33,8 @@
import android.content.ContextWrapper;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.graphics.Matrix;
+import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.IBinder;
@@ -38,6 +42,7 @@
import android.support.test.InstrumentationRegistry;
import android.testing.TestableResources;
import android.view.Display;
+import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.View;
@@ -65,6 +70,9 @@
FakeWindowState mStatusBar;
FakeWindowState mNavigationBar;
+ private boolean mHasDisplayCutout;
+ private int mRotation = ROTATION_0;
+ private final Matrix mTmpMatrix = new Matrix();
@Before
public void setUpBase() throws Exception {
@@ -80,16 +88,32 @@
mPolicy = TestablePhoneWindowManager.create(mContext);
- setRotation(ROTATION_0);
+ updateDisplayFrames();
}
public void setRotation(int rotation) {
+ mRotation = rotation;
+ updateDisplayFrames();
+ }
+
+ private void updateDisplayFrames() {
DisplayInfo info = new DisplayInfo();
- final boolean flippedDimensions = rotation == ROTATION_90 || rotation == ROTATION_270;
+ final boolean flippedDimensions = mRotation == ROTATION_90 || mRotation == ROTATION_270;
info.logicalWidth = flippedDimensions ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
info.logicalHeight = flippedDimensions ? DISPLAY_WIDTH : DISPLAY_HEIGHT;
- info.rotation = rotation;
+ info.rotation = mRotation;
+ if (mHasDisplayCutout) {
+ Path p = new Path();
+ p.addRect(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT,
+ Path.Direction.CCW);
+ transformPhysicalToLogicalCoordinates(
+ mRotation, DISPLAY_WIDTH, DISPLAY_HEIGHT, mTmpMatrix);
+ p.transform(mTmpMatrix);
+ info.displayCutout = DisplayCutout.fromBounds(p);
+ } else {
+ info.displayCutout = null;
+ }
mFrames = new DisplayFrames(Display.DEFAULT_DISPLAY, info);
}
@@ -116,7 +140,8 @@
}
public void addDisplayCutout() {
- mPolicy.mEmulateDisplayCutout = true;
+ mHasDisplayCutout = true;
+ updateDisplayFrames();
}
/** Asserts that {@code actual} is inset by the given amounts from the full display rect. */
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 725fb21..40964c0 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -34,13 +34,13 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import android.app.usage.UsageEvents;
-import android.app.usage.UsageStatsManager;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.ContextWrapper;
@@ -55,6 +55,7 @@
import android.support.test.filters.SmallTest;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
+import android.util.ArraySet;
import android.view.Display;
import com.android.server.SystemService;
@@ -65,7 +66,9 @@
import java.io.File;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.Set;
/**
* Unit test for AppStandbyController.
@@ -78,6 +81,11 @@
private static final String PACKAGE_1 = "com.example.foo";
private static final int UID_1 = 10000;
private static final int USER_ID = 0;
+ private static final int USER_ID2 = 10;
+
+ private static final String ADMIN_PKG = "com.android.admin";
+ private static final String ADMIN_PKG2 = "com.android.admin2";
+ private static final String ADMIN_PKG3 = "com.android.admin3";
private static final long MINUTE_MS = 60 * 1000;
private static final long HOUR_MS = 60 * MINUTE_MS;
@@ -454,6 +462,105 @@
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_FREQUENT);
+ }
+ @Test
+ public void testAddActiveDeviceAdmin() {
+ assertActiveAdmins(USER_ID, (String[]) null);
+ assertActiveAdmins(USER_ID2, (String[]) null);
+
+ mController.addActiveDeviceAdmin(ADMIN_PKG, USER_ID);
+ assertActiveAdmins(USER_ID, ADMIN_PKG);
+ assertActiveAdmins(USER_ID2, (String[]) null);
+
+ mController.addActiveDeviceAdmin(ADMIN_PKG, USER_ID);
+ assertActiveAdmins(USER_ID, ADMIN_PKG);
+ assertActiveAdmins(USER_ID2, (String[]) null);
+
+ mController.addActiveDeviceAdmin(ADMIN_PKG2, USER_ID2);
+ assertActiveAdmins(USER_ID, ADMIN_PKG);
+ assertActiveAdmins(USER_ID2, ADMIN_PKG2);
+ }
+
+ @Test
+ public void testSetActiveAdminApps() {
+ assertActiveAdmins(USER_ID, (String[]) null);
+ assertActiveAdmins(USER_ID2, (String[]) null);
+
+ setActiveAdmins(USER_ID, ADMIN_PKG, ADMIN_PKG2);
+ assertActiveAdmins(USER_ID, ADMIN_PKG, ADMIN_PKG2);
+ assertActiveAdmins(USER_ID2, (String[]) null);
+
+ mController.addActiveDeviceAdmin(ADMIN_PKG2, USER_ID2);
+ setActiveAdmins(USER_ID2, ADMIN_PKG);
+ assertActiveAdmins(USER_ID, ADMIN_PKG, ADMIN_PKG2);
+ assertActiveAdmins(USER_ID2, ADMIN_PKG);
+
+ mController.setActiveAdminApps(null, USER_ID);
+ assertActiveAdmins(USER_ID, (String[]) null);
+ }
+
+ @Test
+ public void isActiveDeviceAdmin() {
+ assertActiveAdmins(USER_ID, (String[]) null);
+ assertActiveAdmins(USER_ID2, (String[]) null);
+
+ mController.addActiveDeviceAdmin(ADMIN_PKG, USER_ID);
+ assertIsActiveAdmin(ADMIN_PKG, USER_ID);
+ assertIsNotActiveAdmin(ADMIN_PKG, USER_ID2);
+
+ mController.addActiveDeviceAdmin(ADMIN_PKG2, USER_ID2);
+ mController.addActiveDeviceAdmin(ADMIN_PKG, USER_ID2);
+ assertIsActiveAdmin(ADMIN_PKG, USER_ID);
+ assertIsNotActiveAdmin(ADMIN_PKG2, USER_ID);
+ assertIsActiveAdmin(ADMIN_PKG, USER_ID2);
+ assertIsActiveAdmin(ADMIN_PKG2, USER_ID2);
+
+ setActiveAdmins(USER_ID2, ADMIN_PKG2);
+ assertIsActiveAdmin(ADMIN_PKG2, USER_ID2);
+ assertIsNotActiveAdmin(ADMIN_PKG, USER_ID2);
+ assertIsActiveAdmin(ADMIN_PKG, USER_ID);
+ assertIsNotActiveAdmin(ADMIN_PKG2, USER_ID);
+ }
+
+ private String getAdminAppsStr(int userId) {
+ return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId));
+ }
+
+ private String getAdminAppsStr(int userId, Set<String> adminApps) {
+ return "admin apps for u" + userId + ": "
+ + (adminApps == null ? "null" : Arrays.toString(adminApps.toArray()));
+ }
+
+ private void assertIsActiveAdmin(String adminApp, int userId) {
+ assertTrue(adminApp + " should be an active admin; " + getAdminAppsStr(userId),
+ mController.isActiveDeviceAdmin(adminApp, userId));
+ }
+
+ private void assertIsNotActiveAdmin(String adminApp, int userId) {
+ assertFalse(adminApp + " shouldn't be an active admin; " + getAdminAppsStr(userId),
+ mController.isActiveDeviceAdmin(adminApp, userId));
+ }
+
+ private void assertActiveAdmins(int userId, String... admins) {
+ final Set<String> actualAdminApps = mController.getActiveAdminAppsForTest(userId);
+ if (admins == null) {
+ if (actualAdminApps != null && !actualAdminApps.isEmpty()) {
+ fail("Admin apps should be null; " + getAdminAppsStr(userId, actualAdminApps));
+ }
+ return;
+ }
+ assertEquals("No. of admin apps not equal; " + getAdminAppsStr(userId, actualAdminApps)
+ + "; expected=" + Arrays.toString(admins), admins.length, actualAdminApps.size());
+ final Set<String> adminAppsCopy = new ArraySet<>(actualAdminApps);
+ for (String admin : admins) {
+ adminAppsCopy.remove(admin);
+ }
+ assertTrue("Unexpected admin apps; " + getAdminAppsStr(userId, actualAdminApps)
+ + "; expected=" + Arrays.toString(admins), adminAppsCopy.isEmpty());
+ }
+
+ private void setActiveAdmins(int userId, String... admins) {
+ mController.setActiveAdminApps(new ArraySet<>(Arrays.asList(admins)), userId);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
index 77f96ca..e7e55cd 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
@@ -16,9 +16,9 @@
package com.android.server.wm;
-import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
-import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_GOING_AWAY;
-import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static org.junit.Assert.assertEquals;
import android.content.Context;
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index 693264c..2284bbb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -37,13 +37,18 @@
import android.annotation.SuppressLint;
import android.content.res.Configuration;
+import android.graphics.Path;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.DisplayMetrics;
import android.util.SparseIntArray;
+import android.view.DisplayCutout;
import android.view.MotionEvent;
+import android.view.Surface;
import java.util.Arrays;
import java.util.LinkedList;
@@ -53,7 +58,7 @@
* Tests for the {@link DisplayContent} class.
*
* Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.wm.DisplayContentTests
+ * atest com.android.server.wm.DisplayContentTests
*/
@SmallTest
@Presubmit
@@ -385,6 +390,38 @@
}
@Test
+ public void testDisplayCutout_rot0() throws Exception {
+ synchronized (sWm.getWindowManagerLock()) {
+ final DisplayContent dc = createNewDisplay();
+ dc.mInitialDisplayWidth = 200;
+ dc.mInitialDisplayHeight = 400;
+ final DisplayCutout cutout = createCutout(new Rect(80, 0, 120, 10));
+
+ dc.mInitialDisplayCutout = cutout;
+ dc.setRotation(Surface.ROTATION_0);
+ dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo.
+
+ assertEquals(cutout, dc.getDisplayInfo().displayCutout);
+ }
+ }
+
+ @Test
+ public void testDisplayCutout_rot90() throws Exception {
+ synchronized (sWm.getWindowManagerLock()) {
+ final DisplayContent dc = createNewDisplay();
+ dc.mInitialDisplayWidth = 200;
+ dc.mInitialDisplayHeight = 400;
+ final DisplayCutout cutout = createCutout(new Rect(80, 0, 120, 10));
+
+ dc.mInitialDisplayCutout = cutout;
+ dc.setRotation(Surface.ROTATION_90);
+ dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo.
+
+ assertEquals(createCutout(new Rect(0, 80, 10, 120)), dc.getDisplayInfo().displayCutout);
+ }
+ }
+
+ @Test
@SuppressLint("InlinedApi")
public void testOrientationDefinedByKeyguard() {
final DisplayContent dc = createNewDisplay();
@@ -449,4 +486,10 @@
y,
metaState);
}
+
+ private DisplayCutout createCutout(Rect r) {
+ Path p = new Path();
+ p.addRect(r.left, r.top, r.right, r.bottom, Path.Direction.CCW);
+ return DisplayCutout.fromBounds(p);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
index ce76a22..ac29163 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
@@ -16,27 +16,38 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
+import android.content.ClipData;
import android.os.IBinder;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.os.UserManagerInternal;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.InputChannel;
import android.view.Surface;
import android.view.SurfaceSession;
+import android.view.View;
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+
/**
* Tests for the {@link DragDropController} class.
*
@@ -46,36 +57,92 @@
@RunWith(AndroidJUnit4.class)
@Presubmit
public class DragDropControllerTests extends WindowTestsBase {
- private static final int TIMEOUT_MS = 1000;
- private DragDropController mTarget;
+ private static final int TIMEOUT_MS = 3000;
+ private TestDragDropController mTarget;
private WindowState mWindow;
private IBinder mToken;
+ static class TestDragDropController extends DragDropController {
+ @GuardedBy("sWm.mWindowMap")
+ private Runnable mCloseCallback;
+
+ TestDragDropController(WindowManagerService service, Looper looper) {
+ super(service, looper);
+ }
+
+ void setOnClosedCallbackLocked(Runnable runnable) {
+ assertTrue(dragDropActiveLocked());
+ mCloseCallback = runnable;
+ }
+
+ @Override
+ void onDragStateClosedLocked(DragState dragState) {
+ super.onDragStateClosedLocked(dragState);
+ if (mCloseCallback != null) {
+ mCloseCallback.run();
+ mCloseCallback = null;
+ }
+ }
+ }
+
+ /**
+ * Creates a window state which can be used as a drop target.
+ */
+ private WindowState createDropTargetWindow(String name, int ownerId) {
+ final WindowTestUtils.TestAppWindowToken token = new WindowTestUtils.TestAppWindowToken(
+ mDisplayContent);
+ final TaskStack stack = createStackControllerOnStackOnDisplay(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer;
+ final Task task = createTaskInStack(stack, ownerId);
+ task.addChild(token, 0);
+
+ final WindowState window = createWindow(
+ null, TYPE_BASE_APPLICATION, token, name, ownerId, false);
+ window.mInputChannel = new InputChannel();
+ window.mHasSurface = true;
+ return window;
+ }
+
@Before
public void setUp() throws Exception {
+ final UserManagerInternal userManager = mock(UserManagerInternal.class);
+ LocalServices.addService(UserManagerInternal.class, userManager);
+
super.setUp();
- assertNotNull(sWm.mDragDropController);
- mTarget = sWm.mDragDropController;
- mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window");
+
+ mTarget = new TestDragDropController(sWm, sWm.mH.getLooper());
+ mDisplayContent = spy(mDisplayContent);
+ mWindow = createDropTargetWindow("Drag test window", 0);
+ when(mDisplayContent.getTouchableWinAtPointLocked(0, 0)).thenReturn(mWindow);
+ when(sWm.mInputManager.transferTouchFocus(any(), any())).thenReturn(true);
+
synchronized (sWm.mWindowMap) {
- // Because sWm is a static object, the previous operation may remain.
- assertFalse(mTarget.dragDropActiveLocked());
+ sWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
}
}
@After
- public void tearDown() {
- if (mToken != null) {
- mTarget.cancelDragAndDrop(mToken);
+ public void tearDown() throws Exception {
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ final CountDownLatch latch;
+ synchronized (sWm.mWindowMap) {
+ if (!mTarget.dragDropActiveLocked()) {
+ return;
+ }
+ if (mToken != null) {
+ mTarget.cancelDragAndDrop(mToken);
+ }
+ latch = new CountDownLatch(1);
+ mTarget.setOnClosedCallbackLocked(() -> {
+ latch.countDown();
+ });
}
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
@Test
- public void testPrepareDrag() throws Exception {
- final Surface surface = new Surface();
- mToken = mTarget.prepareDrag(
- new SurfaceSession(), 0, 0, mWindow.mClient, 0, 100, 100, surface);
- assertNotNull(mToken);
+ public void testDragFlow() throws Exception {
+ dragFlow(0, ClipData.newPlainText("label", "Test"), 0, 0);
}
@Test
@@ -85,4 +152,33 @@
new SurfaceSession(), 0, 0, mWindow.mClient, 0, 0, 0, surface);
assertNull(mToken);
}
+
+ @Test
+ public void testPerformDrag_NullDataWithGrantUri() throws Exception {
+ dragFlow(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ, null, 0, 0);
+ }
+
+ @Test
+ public void testPerformDrag_NullDataToOtherUser() throws Exception {
+ final WindowState otherUsersWindow =
+ createDropTargetWindow("Other user's window", 1 * UserHandle.PER_USER_RANGE);
+ when(mDisplayContent.getTouchableWinAtPointLocked(10, 10))
+ .thenReturn(otherUsersWindow);
+
+ dragFlow(0, null, 10, 10);
+ }
+
+ private void dragFlow(int flag, ClipData data, float dropX, float dropY) {
+ final Surface surface = new Surface();
+ mToken = mTarget.prepareDrag(
+ new SurfaceSession(), 0, 0, mWindow.mClient, flag, 100, 100, surface);
+ assertNotNull(mToken);
+
+ assertTrue(sWm.mInputManager.transferTouchFocus(null, null));
+ assertTrue(mTarget.performDrag(
+ mWindow.mClient, mToken, 0, 0, 0, 0, 0, data));
+
+ mTarget.handleMotionEvent(false, dropX, dropY);
+ mToken = mWindow.mClient.asBinder();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
new file mode 100644
index 0000000..897be34
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.platform.test.annotations.Postsubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+
+import com.android.server.testutils.OffsettableClock;
+import com.android.server.testutils.TestHandler;
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+
+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;
+
+/**
+ * atest FrameworksServicesTests:com.android.server.wm.RemoteAnimationControllerTest
+ */
+@SmallTest
+@FlakyTest(detail = "Promote to presubmit if non-flakyness is established")
+@RunWith(AndroidJUnit4.class)
+public class RemoteAnimationControllerTest extends WindowTestsBase {
+
+ @Mock SurfaceControl mMockLeash;
+ @Mock Transaction mMockTransaction;
+ @Mock OnAnimationFinishedCallback mFinishedCallback;
+ @Mock IRemoteAnimationRunner mMockRunner;
+ private RemoteAnimationAdapter mAdapter;
+ private RemoteAnimationController mController;
+ private final OffsettableClock mClock = new OffsettableClock.Stopped();
+ private TestHandler mHandler;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+ mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50);
+ sWm.mH.runWithScissors(() -> {
+ mHandler = new TestHandler(null, mClock);
+ }, 0);
+ mController = new RemoteAnimationController(sWm, mAdapter, mHandler);
+ }
+
+ @Test
+ public void testRun() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ sWm.mOpeningApps.add(win.mAppToken);
+ try {
+ final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150));
+ adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ mController.goodToGo();
+
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(appsCaptor.capture(), finishedCaptor.capture());
+ assertEquals(1, appsCaptor.getValue().length);
+ final RemoteAnimationTarget app = appsCaptor.getValue()[0];
+ assertEquals(new Point(50, 100), app.position);
+ assertEquals(new Rect(50, 100, 150, 150), app.sourceContainerBounds);
+ assertEquals(win.mAppToken.getPrefixOrderIndex(), app.prefixOrderIndex);
+ assertEquals(win.mAppToken.getTask().mTaskId, app.taskId);
+ assertEquals(mMockLeash, app.leash);
+ assertEquals(win.mWinAnimator.mLastClipRect, app.clipRect);
+ assertEquals(false, app.isTranslucent);
+ verify(mMockTransaction).setLayer(mMockLeash, app.prefixOrderIndex);
+ verify(mMockTransaction).setPosition(mMockLeash, app.position.x, app.position.y);
+ verify(mMockTransaction).setWindowCrop(mMockLeash, new Rect(0, 0, 100, 50));
+
+ finishedCaptor.getValue().onAnimationFinished();
+ verify(mFinishedCallback).onAnimationFinished(eq(adapter));
+ } finally {
+ sWm.mOpeningApps.clear();
+ }
+ }
+
+ @Test
+ public void testCancel() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150));
+ adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ mController.goodToGo();
+
+ adapter.onAnimationCancelled(mMockLeash);
+ verify(mMockRunner).onAnimationCancelled();
+ }
+
+ @Test
+ public void testTimeout() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150));
+ adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ mController.goodToGo();
+
+ mClock.fastForward(2500);
+ mHandler.timeAdvance();
+
+ verify(mMockRunner).onAnimationCancelled();
+ verify(mFinishedCallback).onAnimationFinished(eq(adapter));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index f253632..920796e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -18,7 +18,7 @@
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
-import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
+import static android.view.WindowManager.TRANSIT_UNSET;
import static com.android.server.wm.TaskSnapshotController.*;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index c735341..5ed17cc 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -416,7 +416,8 @@
}
@Override
- public void dismissKeyguardLw(@Nullable IKeyguardDismissCallback callback) {
+ public void dismissKeyguardLw(@Nullable IKeyguardDismissCallback callback,
+ CharSequence message) {
}
@Override
@@ -588,6 +589,7 @@
return false;
}
+ @NavigationBarPosition
@Override
public int getNavBarPosition() {
return NAV_BAR_BOTTOM;
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 7be203a..6a4710b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -223,6 +223,19 @@
assertFalse(app.canAffectSystemUiFlags());
}
+ @Test
+ public void testIsSelfOrAncestorWindowAnimating() throws Exception {
+ final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
+ final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1");
+ final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2");
+ assertFalse(child2.isSelfOrAncestorWindowAnimatingExit());
+ child2.mAnimatingExit = true;
+ assertTrue(child2.isSelfOrAncestorWindowAnimatingExit());
+ child2.mAnimatingExit = false;
+ root.mAnimatingExit = true;
+ assertTrue(child2.isSelfOrAncestorWindowAnimatingExit());
+ }
+
private void testPrepareWindowToDisplayDuringRelayout(boolean wasVisible) {
final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
root.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index c699a94..69b1378 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -230,20 +230,22 @@
boolean ownerCanAddInternalSystemWindow) {
final WindowToken token = createWindowToken(
dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
- return createWindow(parent, type, token, name, ownerCanAddInternalSystemWindow);
+ return createWindow(parent, type, token, name, 0 /* ownerId */,
+ ownerCanAddInternalSystemWindow);
}
static WindowState createWindow(WindowState parent, int type, WindowToken token, String name) {
- return createWindow(parent, type, token, name, false /* ownerCanAddInternalSystemWindow */);
+ return createWindow(parent, type, token, name, 0 /* ownerId */,
+ false /* ownerCanAddInternalSystemWindow */);
}
static WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
- boolean ownerCanAddInternalSystemWindow) {
+ int ownerId, boolean ownerCanAddInternalSystemWindow) {
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
attrs.setTitle(name);
final WindowState w = new WindowState(sWm, sMockSession, sIWindow, token, parent, OP_NONE,
- 0, attrs, VISIBLE, 0, ownerCanAddInternalSystemWindow);
+ 0, attrs, VISIBLE, ownerId, ownerCanAddInternalSystemWindow);
// TODO: Probably better to make this call in the WindowState ctor to avoid errors with
// adding it to the token...
token.addWindow(w);
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
new file mode 100644
index 0000000..40a10e0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.utils;
+
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.*;
+
+import android.graphics.Matrix;
+import android.graphics.Point;
+import android.graphics.PointF;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ErrorCollector;
+
+public class CoordinateTransformsTest {
+
+ private static final int W = 200;
+ private static final int H = 400;
+
+ private final Matrix mMatrix = new Matrix();
+
+ @Rule
+ public final ErrorCollector mErrorCollector = new ErrorCollector();
+
+ @Before
+ public void setUp() throws Exception {
+ mMatrix.setTranslate(0xdeadbeef, 0xdeadbeef);
+ }
+
+ @Test
+ public void transformPhysicalToLogicalCoordinates_rot0() throws Exception {
+ transformPhysicalToLogicalCoordinates(ROTATION_0, W, H, mMatrix);
+ assertThat(mMatrix, is(Matrix.IDENTITY_MATRIX));
+ }
+
+ @Test
+ public void transformPhysicalToLogicalCoordinates_rot90() throws Exception {
+ transformPhysicalToLogicalCoordinates(ROTATION_90, W, H, mMatrix);
+
+ checkDevicePoint(0, 0).mapsToLogicalPoint(0, W);
+ checkDevicePoint(W, H).mapsToLogicalPoint(H, 0);
+ }
+
+ @Test
+ public void transformPhysicalToLogicalCoordinates_rot180() throws Exception {
+ transformPhysicalToLogicalCoordinates(ROTATION_180, W, H, mMatrix);
+
+ checkDevicePoint(0, 0).mapsToLogicalPoint(W, H);
+ checkDevicePoint(W, H).mapsToLogicalPoint(0, 0);
+ }
+
+ @Test
+ public void transformPhysicalToLogicalCoordinates_rot270() throws Exception {
+ transformPhysicalToLogicalCoordinates(ROTATION_270, W, H, mMatrix);
+
+ checkDevicePoint(0, 0).mapsToLogicalPoint(H, 0);
+ checkDevicePoint(W, H).mapsToLogicalPoint(0, W);
+ }
+
+ private DevicePointAssertable checkDevicePoint(int x, int y) {
+ final Point devicePoint = new Point(x, y);
+ final float[] fs = new float[] {x, y};
+ mMatrix.mapPoints(fs);
+ final PointF transformedPoint = new PointF(fs[0], fs[1]);
+
+ return (expectedX, expectedY) -> {
+ mErrorCollector.checkThat("t(" + devicePoint + ")",
+ transformedPoint, is(new PointF(expectedX, expectedY)));
+ };
+ }
+
+ public interface DevicePointAssertable {
+ void mapsToLogicalPoint(int x, int y);
+ }
+}
\ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
new file mode 100644
index 0000000..689c2ce
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
+import android.os.UserManager;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+import com.android.server.UiServiceTestCase;
+import com.android.server.notification.NotificationManagerService.NotificationAssistants;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+public class NotificationAssistantsTest extends UiServiceTestCase {
+
+ @Mock
+ private PackageManager mPm;
+ @Mock
+ private IPackageManager miPm;
+ @Mock
+ private UserManager mUm;
+ @Mock
+ NotificationManagerService mNm;
+
+ NotificationAssistants mAssistants;
+
+ @Mock
+ private ManagedServices.UserProfiles mUserProfiles;
+
+ Object mLock = new Object();
+
+ UserInfo mZero = new UserInfo(0, "zero", 0);
+ UserInfo mTen = new UserInfo(10, "ten", 0);
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ getContext().setMockPackageManager(mPm);
+ getContext().addMockSystemService(Context.USER_SERVICE, mUm);
+ mAssistants = spy(mNm.new NotificationAssistants(getContext(), mLock, mUserProfiles, miPm));
+
+ List<ResolveInfo> approved = new ArrayList<>();
+ ResolveInfo resolve = new ResolveInfo();
+ approved.add(resolve);
+ ServiceInfo info = new ServiceInfo();
+ info.packageName = "a";
+ info.name="a";
+ resolve.serviceInfo = info;
+ when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt()))
+ .thenReturn(approved);
+
+ List<UserInfo> users = new ArrayList<>();
+ users.add(mZero);
+ users.add(mTen);
+ users.add(new UserInfo(11, "11", 0));
+ users.add(new UserInfo(12, "12", 0));
+ for (UserInfo user : users) {
+ when(mUm.getUserInfo(eq(user.id))).thenReturn(user);
+ }
+ when(mUm.getUsers()).thenReturn(users);
+ when(mUm.getUsers(anyBoolean())).thenReturn(users);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(new int[] {0, 10, 11, 12});
+ }
+
+ @Test
+ public void testXmlUpgrade() throws Exception {
+ String xml = "<enabled_assistants/>";
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ parser.nextTag();
+ mAssistants.readXml(parser);
+
+ //once per user
+ verify(mNm, times(mUm.getUsers().size())).readDefaultAssistant(anyInt());
+ }
+
+ @Test
+ public void testXmlUpgradeExistingApprovedComponents() throws Exception {
+ String xml = "<enabled_assistants>"
+ + "<service_listing approved=\"b/b\" user=\"10\" primary=\"true\" />"
+ + "</enabled_assistants>";
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ parser.nextTag();
+ mAssistants.readXml(parser);
+
+ // once per user
+ verify(mNm, times(mUm.getUsers().size())).readDefaultAssistant(anyInt());
+ verify(mAssistants, times(1)).addApprovedList(
+ new ComponentName("b", "b").flattenToString(),10, true);
+ }
+
+ @Test
+ public void testXmlUpgradeOnce() throws Exception {
+ String xml = "<enabled_assistants/>";
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ parser.nextTag();
+ mAssistants.readXml(parser);
+
+ XmlSerializer serializer = new FastXmlSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+ serializer.startDocument(null, true);
+ mAssistants.writeXml(serializer, true);
+ serializer.endDocument();
+ serializer.flush();
+
+ //once per user
+ verify(mNm, times(mUm.getUsers().size())).readDefaultAssistant(anyInt());
+
+ Mockito.reset(mNm);
+
+ parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), null);
+ parser.nextTag();
+ mAssistants.readXml(parser);
+
+ //once per user
+ verify(mNm, never()).readDefaultAssistant(anyInt());
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
index 9564ab9..36136a8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
@@ -48,7 +48,6 @@
mScheduleCalendar = new ScheduleCalendar();
mScheduleInfo = new ZenModeConfig.ScheduleInfo();
mScheduleInfo.days = new int[] {1, 2, 3, 4, 5};
- mScheduleCalendar.setSchedule(mScheduleInfo);
}
@Test
@@ -100,6 +99,7 @@
mScheduleInfo.startMinute = 15;
mScheduleInfo.endMinute = 15;
mScheduleInfo.exitAtAlarm = false;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
Calendar expected = new GregorianCalendar();
expected.setTimeInMillis(cal.getTimeInMillis());
@@ -126,6 +126,7 @@
mScheduleInfo.startMinute = 15;
mScheduleInfo.endMinute = 15;
mScheduleInfo.exitAtAlarm = false;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
Calendar expected = new GregorianCalendar();
expected.setTimeInMillis(cal.getTimeInMillis());
@@ -153,6 +154,7 @@
mScheduleInfo.startMinute = 15;
mScheduleInfo.endMinute = 15;
mScheduleInfo.exitAtAlarm = false;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
Calendar expected = new GregorianCalendar();
expected.setTimeInMillis(cal.getTimeInMillis());
@@ -171,6 +173,7 @@
public void testShouldExitForAlarm_settingOff() {
mScheduleInfo.exitAtAlarm = false;
mScheduleInfo.nextAlarm = 1000;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertFalse(mScheduleCalendar.shouldExitForAlarm(1000));
}
@@ -179,6 +182,7 @@
public void testShouldExitForAlarm_beforeAlarm() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 1000;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertFalse(mScheduleCalendar.shouldExitForAlarm(999));
}
@@ -187,6 +191,7 @@
public void testShouldExitForAlarm_noAlarm() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 0;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertFalse(mScheduleCalendar.shouldExitForAlarm(999));
}
@@ -195,6 +200,7 @@
public void testShouldExitForAlarm() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 1000;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertTrue(mScheduleCalendar.shouldExitForAlarm(1000));
}
@@ -203,6 +209,7 @@
public void testMaybeSetNextAlarm_settingOff() {
mScheduleInfo.exitAtAlarm = false;
mScheduleInfo.nextAlarm = 0;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
mScheduleCalendar.maybeSetNextAlarm(1000, 2000);
@@ -213,6 +220,7 @@
public void testMaybeSetNextAlarm_settingOn() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 0;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
mScheduleCalendar.maybeSetNextAlarm(1000, 2000);
@@ -223,6 +231,7 @@
public void testMaybeSetNextAlarm_alarmCanceled() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 10000;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
mScheduleCalendar.maybeSetNextAlarm(1000, 0);
@@ -233,6 +242,7 @@
public void testMaybeSetNextAlarm_earlierAlarm() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 2000;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
mScheduleCalendar.maybeSetNextAlarm(1000, 1500);
@@ -242,6 +252,7 @@
@Test
public void testMaybeSetNextAlarm_laterAlarm() {
mScheduleInfo.exitAtAlarm = true;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
mScheduleInfo.nextAlarm = 2000;
mScheduleCalendar.maybeSetNextAlarm(1000, 3000);
@@ -253,6 +264,7 @@
public void testMaybeSetNextAlarm_expiredAlarm() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 998;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
mScheduleCalendar.maybeSetNextAlarm(1000, 999);
@@ -272,6 +284,7 @@
mScheduleInfo.endHour = 3;
mScheduleInfo.startMinute = 15;
mScheduleInfo.endMinute = 15;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertTrue(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
}
@@ -289,6 +302,7 @@
mScheduleInfo.endHour = 3;
mScheduleInfo.startMinute = 16;
mScheduleInfo.endMinute = 15;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertTrue(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
}
@@ -306,6 +320,7 @@
mScheduleInfo.startMinute = 16;
mScheduleInfo.endHour = 15;
mScheduleInfo.endMinute = 15;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertFalse(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
}
@@ -322,6 +337,7 @@
mScheduleInfo.endHour = 3;
mScheduleInfo.startMinute = 16;
mScheduleInfo.endMinute = 15;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
assertFalse(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
index 58f0ded..a60d715 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
@@ -47,7 +47,7 @@
public void testSingleString() throws Exception {
String[] expected = { "foobar" };
Bundle bundle = new Bundle();
- bundle.putString(Notification.EXTRA_PEOPLE, expected[0]);
+ bundle.putString(Notification.EXTRA_PEOPLE_LIST, expected[0]);
String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
assertStringArrayEquals("string should be in result[0]", expected, result);
}
@@ -56,7 +56,7 @@
public void testSingleCharArray() throws Exception {
String[] expected = { "foobar" };
Bundle bundle = new Bundle();
- bundle.putCharArray(Notification.EXTRA_PEOPLE, expected[0].toCharArray());
+ bundle.putCharArray(Notification.EXTRA_PEOPLE_LIST, expected[0].toCharArray());
String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
assertStringArrayEquals("char[] should be in result[0]", expected, result);
}
@@ -65,7 +65,7 @@
public void testSingleCharSequence() throws Exception {
String[] expected = { "foobar" };
Bundle bundle = new Bundle();
- bundle.putCharSequence(Notification.EXTRA_PEOPLE, new SpannableString(expected[0]));
+ bundle.putCharSequence(Notification.EXTRA_PEOPLE_LIST, new SpannableString(expected[0]));
String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
assertStringArrayEquals("charSequence should be in result[0]", expected, result);
}
@@ -74,7 +74,7 @@
public void testStringArraySingle() throws Exception {
Bundle bundle = new Bundle();
String[] expected = { "foobar" };
- bundle.putStringArray(Notification.EXTRA_PEOPLE, expected);
+ bundle.putStringArray(Notification.EXTRA_PEOPLE_LIST, expected);
String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
assertStringArrayEquals("wrapped string should be in result[0]", expected, result);
}
@@ -83,7 +83,7 @@
public void testStringArrayMultiple() throws Exception {
Bundle bundle = new Bundle();
String[] expected = { "foo", "bar", "baz" };
- bundle.putStringArray(Notification.EXTRA_PEOPLE, expected);
+ bundle.putStringArray(Notification.EXTRA_PEOPLE_LIST, expected);
String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
assertStringArrayEquals("testStringArrayMultiple", expected, result);
}
@@ -92,7 +92,7 @@
public void testStringArrayNulls() throws Exception {
Bundle bundle = new Bundle();
String[] expected = { "foo", null, "baz" };
- bundle.putStringArray(Notification.EXTRA_PEOPLE, expected);
+ bundle.putStringArray(Notification.EXTRA_PEOPLE_LIST, expected);
String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
assertStringArrayEquals("testStringArrayNulls", expected, result);
}
@@ -105,7 +105,7 @@
for (int i = 0; i < expected.length; i++) {
charSeqArray[i] = new SpannableString(expected[i]);
}
- bundle.putCharSequenceArray(Notification.EXTRA_PEOPLE, charSeqArray);
+ bundle.putCharSequenceArray(Notification.EXTRA_PEOPLE_LIST, charSeqArray);
String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
assertStringArrayEquals("testCharSequenceArrayMultiple", expected, result);
}
@@ -122,7 +122,7 @@
charSeqArray[i] = new SpannableString(expected[i]);
}
}
- bundle.putCharSequenceArray(Notification.EXTRA_PEOPLE, charSeqArray);
+ bundle.putCharSequenceArray(Notification.EXTRA_PEOPLE_LIST, charSeqArray);
String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
assertStringArrayEquals("testMixedCharSequenceArrayList", expected, result);
}
@@ -135,7 +135,7 @@
for (int i = 0; i < expected.length; i++) {
stringArrayList.add(expected[i]);
}
- bundle.putStringArrayList(Notification.EXTRA_PEOPLE, stringArrayList);
+ bundle.putStringArrayList(Notification.EXTRA_PEOPLE_LIST, stringArrayList);
String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
assertStringArrayEquals("testStringArrayList", expected, result);
}
@@ -149,11 +149,24 @@
for (int i = 0; i < expected.length; i++) {
stringArrayList.add(new SpannableString(expected[i]));
}
- bundle.putCharSequenceArrayList(Notification.EXTRA_PEOPLE, stringArrayList);
+ bundle.putCharSequenceArrayList(Notification.EXTRA_PEOPLE_LIST, stringArrayList);
String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
assertStringArrayEquals("testCharSequenceArrayList", expected, result);
}
+ @Test
+ public void testPeopleArrayList() throws Exception {
+ Bundle bundle = new Bundle();
+ String[] expected = { "name:test" , "tel:1234" };
+ final ArrayList<Notification.Person> arrayList =
+ new ArrayList<>(expected.length);
+ arrayList.add(new Notification.Person().setName("test"));
+ arrayList.add(new Notification.Person().setUri(expected[1]));
+ bundle.putParcelableArrayList(Notification.EXTRA_PEOPLE_LIST, arrayList);
+ String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+ assertStringArrayEquals("testPeopleArrayList", expected, result);
+ }
+
private void assertStringArrayEquals(String message, String[] expected, String[] result) {
String expectedString = Arrays.toString(expected);
String resultString = Arrays.toString(result);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 0c7397a..c532a8a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -107,6 +107,68 @@
}
@Test
+ public void testAlarmsOnly_alarmMediaMuteNotApplied() {
+ mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_ALARMS;
+ mZenModeHelperSpy.mConfig.allowAlarms = false;
+ mZenModeHelperSpy.mConfig.allowMediaSystemOther = false;
+ assertFalse(mZenModeHelperSpy.mConfig.allowAlarms);
+ assertFalse(mZenModeHelperSpy.mConfig.allowMediaSystemOther);
+ mZenModeHelperSpy.applyRestrictions();
+
+ // Alarms only mode will not silence alarms
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ AudioAttributes.USAGE_ALARM);
+
+ // Alarms only mode will not silence media
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ AudioAttributes.USAGE_MEDIA);
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ AudioAttributes.USAGE_GAME);
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ AudioAttributes.USAGE_ASSISTANCE_SONIFICATION);
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ AudioAttributes.USAGE_UNKNOWN);
+ }
+
+ @Test
+ public void testAlarmsOnly_callsMuteApplied() {
+ mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_ALARMS;
+ mZenModeHelperSpy.mConfig.allowCalls = true;
+ assertTrue(mZenModeHelperSpy.mConfig.allowCalls);
+ mZenModeHelperSpy.applyRestrictions();
+
+ // Alarms only mode will silence calls despite priority-mode config
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ AudioAttributes.USAGE_NOTIFICATION_RINGTONE);
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST);
+ }
+
+ @Test
+ public void testAlarmsOnly_allZenConfigToggledCannotBypass_alarmMuteNotApplied() {
+ // Only audio attributes with SUPPRESIBLE_NEVER can bypass
+ mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_ALARMS;
+ mZenModeHelperSpy.mConfig.allowAlarms = false;
+ mZenModeHelperSpy.mConfig.allowMediaSystemOther = false;
+ mZenModeHelperSpy.mConfig.allowReminders = false;
+ mZenModeHelperSpy.mConfig.allowCalls = false;
+ mZenModeHelperSpy.mConfig.allowMessages = false;
+ mZenModeHelperSpy.mConfig.allowEvents = false;
+ mZenModeHelperSpy.mConfig.allowRepeatCallers= false;
+ assertFalse(mZenModeHelperSpy.mConfig.allowAlarms);
+ assertFalse(mZenModeHelperSpy.mConfig.allowMediaSystemOther);
+ assertFalse(mZenModeHelperSpy.mConfig.allowReminders);
+ assertFalse(mZenModeHelperSpy.mConfig.allowCalls);
+ assertFalse(mZenModeHelperSpy.mConfig.allowMessages);
+ assertFalse(mZenModeHelperSpy.mConfig.allowEvents);
+ assertFalse(mZenModeHelperSpy.mConfig.allowRepeatCallers);
+ mZenModeHelperSpy.applyRestrictions();
+
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ AudioAttributes.USAGE_ALARM);
+ }
+
+ @Test
public void testZenAllCannotBypass() {
// Only audio attributes with SUPPRESIBLE_NEVER can bypass
mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 2ec218a..ff3d586 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -33,7 +33,6 @@
import android.app.ActivityManager;
import android.app.AppGlobals;
-import android.app.admin.DevicePolicyManager;
import android.app.usage.UsageStatsManager.StandbyBuckets;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
@@ -66,13 +65,16 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.TelephonyManager;
+import android.util.ArraySet;
import android.util.KeyValueListParser;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.view.Display;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
@@ -87,6 +89,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Manages the standby state of an app, listening to various events.
@@ -147,6 +150,9 @@
@GuardedBy("mAppIdleLock")
private List<String> mCarrierPrivilegedApps;
+ @GuardedBy("mActiveAdminApps")
+ private final SparseArray<Set<String>> mActiveAdminApps = new SparseArray<>();
+
// Messages for the handler
static final int MSG_INFORM_LISTENERS = 3;
static final int MSG_FORCE_IDLE_STATE = 4;
@@ -619,6 +625,9 @@
public void onUserRemoved(int userId) {
synchronized (mAppIdleLock) {
mAppIdleHistory.onUserRemoved(userId);
+ synchronized (mActiveAdminApps) {
+ mActiveAdminApps.remove(userId);
+ }
}
}
@@ -857,10 +866,39 @@
newBucket);
}
- private boolean isActiveDeviceAdmin(String packageName, int userId) {
- DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
- if (dpm == null) return false;
- return dpm.packageHasActiveAdmins(packageName, userId);
+ @VisibleForTesting
+ boolean isActiveDeviceAdmin(String packageName, int userId) {
+ synchronized (mActiveAdminApps) {
+ final Set<String> adminPkgs = mActiveAdminApps.get(userId);
+ return adminPkgs != null && adminPkgs.contains(packageName);
+ }
+ }
+
+ public void addActiveDeviceAdmin(String adminPkg, int userId) {
+ synchronized (mActiveAdminApps) {
+ Set<String> adminPkgs = mActiveAdminApps.get(userId);
+ if (adminPkgs == null) {
+ adminPkgs = new ArraySet<>();
+ mActiveAdminApps.put(userId, adminPkgs);
+ }
+ adminPkgs.add(adminPkg);
+ }
+ }
+
+ public void setActiveAdminApps(Set<String> adminPkgs, int userId) {
+ synchronized (mActiveAdminApps) {
+ if (adminPkgs == null) {
+ mActiveAdminApps.remove(userId);
+ } else {
+ mActiveAdminApps.put(userId, adminPkgs);
+ }
+ }
+ }
+
+ Set<String> getActiveAdminAppsForTest(int userId) {
+ synchronized (mActiveAdminApps) {
+ return mActiveAdminApps.get(userId);
+ }
}
/**
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 463a26e..78cc81f 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -70,6 +70,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* A service that collects, aggregates, and persists application usage data.
@@ -1020,5 +1021,14 @@
public long getTimeSinceLastJobRun(String packageName, int userId) {
return mAppStandby.getTimeSinceLastJobRun(packageName, userId);
}
+
+ public void onActiveAdminAdded(String packageName, int userId) {
+ mAppStandby.addActiveDeviceAdmin(packageName, userId);
+ }
+
+ @Override
+ public void setActiveAdminApps(Set<String> packageNames, int userId) {
+ mAppStandby.setActiveAdminApps(packageNames, userId);
+ }
}
}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 2091101..8c7d6b3 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -1408,7 +1408,7 @@
* @param extras Bundle containing extra information associated with the event.
*/
public void sendCallEvent(String event, Bundle extras) {
- mInCallAdapter.sendCallEvent(mTelecomCallId, event, extras);
+ mInCallAdapter.sendCallEvent(mTelecomCallId, event, mTargetSdkVersion, extras);
}
/**
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index 4bc2a9b1..658685f 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -286,11 +286,12 @@
*
* @param callId The callId to send the event for.
* @param event The event.
+ * @param targetSdkVer Target sdk version of the app calling this api
* @param extras Extras associated with the event.
*/
- public void sendCallEvent(String callId, String event, Bundle extras) {
+ public void sendCallEvent(String callId, String event, int targetSdkVer, Bundle extras) {
try {
- mAdapter.sendCallEvent(callId, event, extras);
+ mAdapter.sendCallEvent(callId, event, targetSdkVer, extras);
} catch (RemoteException ignored) {
}
}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 2d1fe50..d292db3 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -236,6 +237,15 @@
"android.telecom.extra.INCOMING_CALL_EXTRAS";
/**
+ * Optional extra for {@link #ACTION_INCOMING_CALL} containing a boolean to indicate that the
+ * call has an externally generated ringer. Used by the HfpClientConnectionService when In Band
+ * Ringtone is enabled to prevent two ringers from being generated.
+ * @hide
+ */
+ public static final String EXTRA_CALL_EXTERNAL_RINGER =
+ "android.telecom.extra.CALL_EXTERNAL_RINGER";
+
+ /**
* Optional extra for {@link android.content.Intent#ACTION_CALL} and
* {@link android.content.Intent#ACTION_DIAL} {@code Intent} containing a {@link Bundle}
* which contains metadata about the call. This {@link Bundle} will be saved into
@@ -1431,6 +1441,13 @@
public void addNewIncomingCall(PhoneAccountHandle phoneAccount, Bundle extras) {
try {
if (isServiceConnected()) {
+ if (extras != null && extras.getBoolean(EXTRA_IS_HANDOVER) &&
+ mContext.getApplicationContext().getApplicationInfo().targetSdkVersion >
+ Build.VERSION_CODES.O_MR1) {
+ Log.e("TAG", "addNewIncomingCall failed. Use public api " +
+ "acceptHandover for API > O-MR1");
+ // TODO add "return" after DUO team adds support for new handover API
+ }
getTelecomService().addNewIncomingCall(
phoneAccount, extras == null ? new Bundle() : extras);
}
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index 23ac940..87ccd3e 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -64,7 +64,7 @@
void pullExternalCall(String callId);
- void sendCallEvent(String callId, String event, in Bundle extras);
+ void sendCallEvent(String callId, String event, int targetSdkVer, in Bundle extras);
void putExtras(String callId, in Bundle extras);
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index e0b6f61..e633053 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -2564,6 +2564,35 @@
public static final Uri CONTENT_URI = Uri.parse("content://telephony/carriers");
/**
+ * The {@code content://} style URL to be called from DevicePolicyManagerService,
+ * can manage DPC-owned APNs.
+ * @hide
+ */
+ public static final Uri DPC_URI = Uri.parse("content://telephony/carriers/dpc");
+
+ /**
+ * The {@code content://} style URL to be called from Telephony to query APNs.
+ * When DPC-owned APNs are enforced, only DPC-owned APNs are returned, otherwise only
+ * non-DPC-owned APNs are returned.
+ * @hide
+ */
+ public static final Uri FILTERED_URI = Uri.parse("content://telephony/carriers/filtered");
+
+ /**
+ * The {@code content://} style URL to be called from DevicePolicyManagerService
+ * or Telephony to manage whether DPC-owned APNs are enforced.
+ * @hide
+ */
+ public static final Uri ENFORCE_MANAGED_URI = Uri.parse(
+ "content://telephony/carriers/enforce_managed");
+
+ /**
+ * The column name for ENFORCE_MANAGED_URI, indicates whether DPC-owned APNs are enforced.
+ * @hide
+ */
+ public static final String ENFORCE_KEY = "enforced";
+
+ /**
* The default sort order for this table.
*/
public static final String DEFAULT_SORT_ORDER = "name ASC";
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index fccc2a6..ce0b551 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -337,6 +337,19 @@
"notify_handover_video_from_wifi_to_lte_bool";
/**
+ * Flag specifying whether the carrier wants to notify the user when a VT call has been handed
+ * over from LTE to WIFI.
+ * <p>
+ * The handover notification is sent as a
+ * {@link TelephonyManager#EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI}
+ * {@link android.telecom.Connection} event, which an {@link android.telecom.InCallService}
+ * should use to trigger the display of a user-facing message.
+ * @hide
+ */
+ public static final String KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL =
+ "notify_handover_video_from_lte_to_wifi_bool";
+
+ /**
* Flag specifying whether the carrier supports downgrading a video call (tx, rx or tx/rx)
* directly to an audio call.
* @hide
@@ -947,8 +960,9 @@
public static final String KEY_CARRIER_NAME_OVERRIDE_BOOL = "carrier_name_override_bool";
/**
- * String to identify carrier name in CarrierConfig app. This string is used only if
- * #KEY_CARRIER_NAME_OVERRIDE_BOOL is true
+ * String to identify carrier name in CarrierConfig app. This string overrides SPN if
+ * #KEY_CARRIER_NAME_OVERRIDE_BOOL is true; otherwise, it will be used if its value is provided
+ * and SPN is unavailable
* @hide
*/
public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
@@ -1740,6 +1754,13 @@
public static final String KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL =
"check_pricing_with_carrier_data_roaming_bool";
+ /**
+ * List of thresholds of RSRP for determining the display level of LTE signal bar.
+ * @hide
+ */
+ public static final String KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY =
+ "lte_rsrp_thresholds_int_array";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -1756,6 +1777,7 @@
sDefaults.putBoolean(KEY_CARRIER_VOLTE_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VT_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL, false);
+ sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL, true);
sDefaults.putString(KEY_DEFAULT_VM_NUMBER_STRING, "");
sDefaults.putBoolean(KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL, false);
@@ -2028,6 +2050,15 @@
sDefaults.putBoolean(KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false);
+ sDefaults.putIntArray(KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
+ new int[] {
+ -140, /* SIGNAL_STRENGTH_NONE_OR_UNKNOWN */
+ -128, /* SIGNAL_STRENGTH_POOR */
+ -118, /* SIGNAL_STRENGTH_MODERATE */
+ -108, /* SIGNAL_STRENGTH_GOOD */
+ -98, /* SIGNAL_STRENGTH_GREAT */
+ -44
+ });
}
/**
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index c7e5131..98ea451 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -244,6 +244,13 @@
*/
public static final int LISTEN_DATA_ACTIVATION_STATE = 0x00040000;
+ /**
+ * Listen for changes to the user mobile data state
+ *
+ * @see #onUserMobileDataStateChanged
+ */
+ public static final int LISTEN_USER_MOBILE_DATA_STATE = 0x00080000;
+
/*
* Subscription used to listen to the phone state changes
* @hide
@@ -349,6 +356,9 @@
case LISTEN_DATA_ACTIVATION_STATE:
PhoneStateListener.this.onDataActivationStateChanged((int)msg.obj);
break;
+ case LISTEN_USER_MOBILE_DATA_STATE:
+ PhoneStateListener.this.onUserMobileDataStateChanged((boolean)msg.obj);
+ break;
case LISTEN_CARRIER_NETWORK_CHANGE:
PhoneStateListener.this.onCarrierNetworkChange((boolean)msg.obj);
break;
@@ -543,6 +553,14 @@
}
/**
+ * Callback invoked when the user mobile data state has changed
+ * @param enabled indicates whether the current user mobile data state is enabled or disabled.
+ */
+ public void onUserMobileDataStateChanged(boolean enabled) {
+ // default implementation empty
+ }
+
+ /**
* Callback invoked when telephony has received notice from a carrier
* app that a network action that could result in connectivity loss
* has been requested by an app using
@@ -654,6 +672,10 @@
send(LISTEN_DATA_ACTIVATION_STATE, 0, 0, activationState);
}
+ public void onUserMobileDataStateChanged(boolean enabled) {
+ send(LISTEN_USER_MOBILE_DATA_STATE, 0, 0, enabled);
+ }
+
public void onCarrierNetworkChange(boolean active) {
send(LISTEN_CARRIER_NETWORK_CHANGE, 0, 0, active);
}
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index de02de7..d2134f9 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -19,9 +19,13 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.CarrierConfigManager;
import android.util.Log;
import android.content.res.Resources;
+import java.util.ArrayList;
+import java.util.Arrays;
+
/**
* Contains phone signal strength related information.
*/
@@ -51,6 +55,8 @@
//Use int max, as -1 is a valid value in signal strength
public static final int INVALID = 0x7FFFFFFF;
+ private static final int LTE_RSRP_THRESHOLDS_NUM = 6;
+
private int mGsmSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
private int mGsmBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
private int mCdmaDbm; // This value is the RSSI value
@@ -70,6 +76,9 @@
private boolean isGsm; // This value is set by the ServiceStateTracker onSignalStrengthResult
private boolean mUseOnlyRsrpForLteLevel; // Use only RSRP for the number of LTE signal bar.
+ // The threshold of LTE RSRP for determining the display level of LTE signal bar.
+ private int mLteRsrpThresholds[] = new int[LTE_RSRP_THRESHOLDS_NUM];
+
/**
* Create a new SignalStrength from a intent notifier Bundle
*
@@ -110,6 +119,7 @@
mTdScdmaRscp = INVALID;
isGsm = true;
mUseOnlyRsrpForLteLevel = false;
+ setLteRsrpThresholds(getDefaultLteRsrpThresholds());
}
/**
@@ -137,6 +147,7 @@
mTdScdmaRscp = INVALID;
isGsm = gsmFlag;
mUseOnlyRsrpForLteLevel = false;
+ setLteRsrpThresholds(getDefaultLteRsrpThresholds());
}
/**
@@ -276,6 +287,8 @@
mTdScdmaRscp = INVALID;
isGsm = gsm;
mUseOnlyRsrpForLteLevel = useOnlyRsrpForLteLevel;
+
+ setLteRsrpThresholds(getDefaultLteRsrpThresholds());
if (DBG) log("initialize: " + toString());
}
@@ -299,6 +312,7 @@
mTdScdmaRscp = s.mTdScdmaRscp;
isGsm = s.isGsm;
mUseOnlyRsrpForLteLevel = s.mUseOnlyRsrpForLteLevel;
+ setLteRsrpThresholds(s.mLteRsrpThresholds);
}
/**
@@ -325,6 +339,9 @@
mTdScdmaRscp = in.readInt();
isGsm = (in.readInt() != 0);
mUseOnlyRsrpForLteLevel = (in.readInt() != 0);
+ for (int i = 0; i < LTE_RSRP_THRESHOLDS_NUM; i++) {
+ mLteRsrpThresholds[i] = in.readInt();
+ }
}
/**
@@ -374,6 +391,9 @@
out.writeInt(mTdScdmaRscp);
out.writeInt(isGsm ? 1 : 0);
out.writeInt(mUseOnlyRsrpForLteLevel ? 1 : 0);
+ for (int i = 0; i < LTE_RSRP_THRESHOLDS_NUM; i++) {
+ out.writeInt(mLteRsrpThresholds[i]);
+ }
}
/**
@@ -480,6 +500,22 @@
}
/**
+ * Sets the threshold array for determining the display level of LTE signal bar.
+ *
+ * @param lteRsrpThresholds int array for determining the display level.
+ *
+ * @hide
+ */
+ public void setLteRsrpThresholds(int[] lteRsrpThresholds) {
+ if ((lteRsrpThresholds == null)
+ || (lteRsrpThresholds.length != LTE_RSRP_THRESHOLDS_NUM)) {
+ Log.wtf(LOG_TAG, "setLteRsrpThresholds - lteRsrpThresholds is invalid.");
+ return;
+ }
+ System.arraycopy(lteRsrpThresholds, 0, mLteRsrpThresholds, 0, LTE_RSRP_THRESHOLDS_NUM);
+ }
+
+ /**
* Get the GSM Signal Strength, valid values are (0-31, 99) as defined in TS
* 27.007 8.5
*/
@@ -833,25 +869,18 @@
*/
int rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, rsrpIconLevel = -1, snrIconLevel = -1;
- int[] threshRsrp = Resources.getSystem().getIntArray(
- com.android.internal.R.array.config_lteDbmThresholds);
- if (threshRsrp.length != 6) {
- Log.wtf(LOG_TAG, "getLteLevel - config_lteDbmThresholds has invalid num of elements."
- + " Cannot evaluate RSRP signal.");
- } else {
- if (mLteRsrp > threshRsrp[5]) {
- rsrpIconLevel = -1;
- } else if (mLteRsrp >= (threshRsrp[4] - mLteRsrpBoost)) {
- rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
- } else if (mLteRsrp >= (threshRsrp[3] - mLteRsrpBoost)) {
- rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
- } else if (mLteRsrp >= (threshRsrp[2] - mLteRsrpBoost)) {
- rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
- } else if (mLteRsrp >= (threshRsrp[1] - mLteRsrpBoost)) {
- rsrpIconLevel = SIGNAL_STRENGTH_POOR;
- } else if (mLteRsrp >= threshRsrp[0]) {
- rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- }
+ if (mLteRsrp > mLteRsrpThresholds[5]) {
+ rsrpIconLevel = -1;
+ } else if (mLteRsrp >= (mLteRsrpThresholds[4] - mLteRsrpBoost)) {
+ rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
+ } else if (mLteRsrp >= (mLteRsrpThresholds[3] - mLteRsrpBoost)) {
+ rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
+ } else if (mLteRsrp >= (mLteRsrpThresholds[2] - mLteRsrpBoost)) {
+ rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
+ } else if (mLteRsrp >= (mLteRsrpThresholds[1] - mLteRsrpBoost)) {
+ rsrpIconLevel = SIGNAL_STRENGTH_POOR;
+ } else if (mLteRsrp >= mLteRsrpThresholds[0]) {
+ rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
}
if (useOnlyRsrpForLteLevel()) {
@@ -1010,7 +1039,7 @@
+ (mLteSignalStrength * primeNum) + (mLteRsrp * primeNum)
+ (mLteRsrq * primeNum) + (mLteRssnr * primeNum) + (mLteCqi * primeNum)
+ (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum) + (isGsm ? 1 : 0)
- + (mUseOnlyRsrpForLteLevel ? 1 : 0));
+ + (mUseOnlyRsrpForLteLevel ? 1 : 0) + (Arrays.hashCode(mLteRsrpThresholds)));
}
/**
@@ -1045,7 +1074,8 @@
&& mLteRsrpBoost == s.mLteRsrpBoost
&& mTdScdmaRscp == s.mTdScdmaRscp
&& isGsm == s.isGsm
- && mUseOnlyRsrpForLteLevel == s.mUseOnlyRsrpForLteLevel);
+ && mUseOnlyRsrpForLteLevel == s.mUseOnlyRsrpForLteLevel
+ && Arrays.equals(mLteRsrpThresholds, s.mLteRsrpThresholds));
}
/**
@@ -1070,7 +1100,8 @@
+ " " + mTdScdmaRscp
+ " " + (isGsm ? "gsm|lte" : "cdma")
+ " " + (mUseOnlyRsrpForLteLevel ? "use_only_rsrp_for_lte_level" :
- "use_rsrp_and_rssnr_for_lte_level"));
+ "use_rsrp_and_rssnr_for_lte_level")
+ + " " + (Arrays.toString(mLteRsrpThresholds)));
}
/** Returns the signal strength related to GSM. */
@@ -1126,6 +1157,10 @@
mTdScdmaRscp = m.getInt("TdScdma");
isGsm = m.getBoolean("isGsm");
mUseOnlyRsrpForLteLevel = m.getBoolean("useOnlyRsrpForLteLevel");
+ ArrayList<Integer> lteRsrpThresholds = m.getIntegerArrayList("lteRsrpThresholds");
+ for (int i = 0; i < lteRsrpThresholds.size(); i++) {
+ mLteRsrpThresholds[i] = lteRsrpThresholds.get(i);
+ }
}
/**
@@ -1151,6 +1186,21 @@
m.putInt("TdScdma", mTdScdmaRscp);
m.putBoolean("isGsm", isGsm);
m.putBoolean("useOnlyRsrpForLteLevel", mUseOnlyRsrpForLteLevel);
+ ArrayList<Integer> lteRsrpThresholds = new ArrayList<Integer>();
+ for (int value : mLteRsrpThresholds) {
+ lteRsrpThresholds.add(value);
+ }
+ m.putIntegerArrayList("lteRsrpThresholds", lteRsrpThresholds);
+ }
+
+ /**
+ * Gets the default threshold array for determining the display level of LTE signal bar.
+ *
+ * @return int array for determining the display level.
+ */
+ private int[] getDefaultLteRsrpThresholds() {
+ return CarrierConfigManager.getDefaultConfig().getIntArray(
+ CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY);
}
/**
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2396a9e..2fafdf5 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -18,10 +18,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.app.BroadcastOptions;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -36,6 +38,7 @@
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.util.DisplayMetrics;
+import android.util.Log;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
import com.android.internal.telephony.ISub;
@@ -46,6 +49,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.TimeUnit;
/**
* SubscriptionManager is the application interface to SubscriptionController
@@ -455,6 +459,39 @@
= "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS";
/**
+ * Broadcast Action: Request a refresh of the billing relationship plans
+ * between a carrier and a specific subscriber.
+ * <p>
+ * Carrier apps are encouraged to implement this receiver, and the OS will
+ * provide an affordance to request a refresh. This affordance will only be
+ * shown when the carrier app is actively providing subscription plan
+ * information via {@link #setSubscriptionPlans(int, List)}.
+ * <p>
+ * Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to indicate which subscription
+ * the user is interested in.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @SystemApi
+ public static final String ACTION_REFRESH_SUBSCRIPTION_PLANS
+ = "android.telephony.action.REFRESH_SUBSCRIPTION_PLANS";
+
+ /**
+ * Broadcast Action: The billing relationship plans between a carrier and a
+ * specific subscriber has changed.
+ * <p>
+ * Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to indicate which subscription
+ * changed.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS)
+ public static final String ACTION_SUBSCRIPTION_PLANS_CHANGED
+ = "android.telephony.action.SUBSCRIPTION_PLANS_CHANGED";
+
+ /**
* Integer extra used with {@link #ACTION_DEFAULT_SUBSCRIPTION_CHANGED} and
* {@link #ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED} to indicate the subscription
* which has changed.
@@ -1637,7 +1674,7 @@
* This method is only accessible to the following narrow set of apps:
* <ul>
* <li>The carrier app for this subscriberId, as determined by
- * {@link TelephonyManager#hasCarrierPrivileges(int)}.
+ * {@link TelephonyManager#hasCarrierPrivileges()}.
* <li>The carrier app explicitly delegated access through
* {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
* </ul>
@@ -1664,7 +1701,7 @@
* This method is only accessible to the following narrow set of apps:
* <ul>
* <li>The carrier app for this subscriberId, as determined by
- * {@link TelephonyManager#hasCarrierPrivileges(int)}.
+ * {@link TelephonyManager#hasCarrierPrivileges()}.
* <li>The carrier app explicitly delegated access through
* {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
* </ul>
@@ -1695,8 +1732,8 @@
}
/**
- * Create an {@link Intent} that will launch towards the carrier app that is
- * currently defining the billing relationship plan through
+ * Create an {@link Intent} that can be launched towards the carrier app
+ * that is currently defining the billing relationship plan through
* {@link #setSubscriptionPlans(int, List)}.
*
* @return ready to launch Intent targeted towards the carrier app, or
@@ -1725,4 +1762,55 @@
return intent;
}
+
+ /** @hide */
+ private @Nullable Intent createRefreshSubscriptionIntent(int subId) {
+ // Bail if no owner
+ final String owner = getSubscriptionPlansOwner(subId);
+ if (owner == null) return null;
+
+ // Bail if no plans
+ final List<SubscriptionPlan> plans = getSubscriptionPlans(subId);
+ if (plans.isEmpty()) return null;
+
+ final Intent intent = new Intent(ACTION_REFRESH_SUBSCRIPTION_PLANS);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.setPackage(owner);
+ intent.putExtra(EXTRA_SUBSCRIPTION_INDEX, subId);
+
+ // Bail if not implemented
+ if (mContext.getPackageManager().queryBroadcastReceivers(intent, 0).isEmpty()) {
+ return null;
+ }
+
+ return intent;
+ }
+
+ /**
+ * Check if there is a carrier app that is currently defining the billing
+ * relationship plan through {@link #setSubscriptionPlans(int, List)} that
+ * supports refreshing of subscription plans.
+ *
+ * @hide
+ */
+ public boolean isSubscriptionPlansRefreshSupported(int subId) {
+ return createRefreshSubscriptionIntent(subId) != null;
+ }
+
+ /**
+ * Request that the carrier app that is currently defining the billing
+ * relationship plan through {@link #setSubscriptionPlans(int, List)}
+ * refresh its subscription plans.
+ * <p>
+ * If the app is able to successfully update the plans, you'll expect to
+ * receive the {@link #ACTION_SUBSCRIPTION_PLANS_CHANGED} broadcast.
+ *
+ * @hide
+ */
+ public void requestSubscriptionPlansRefresh(int subId) {
+ final Intent intent = createRefreshSubscriptionIntent(subId);
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setTemporaryAppWhitelistDuration(TimeUnit.MINUTES.toMillis(1));
+ mContext.sendBroadcast(intent, null, options.toBundle());
+ }
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index af5b190..8edc8b1 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -53,6 +53,7 @@
import com.android.ims.internal.IImsMMTelFeature;
import com.android.ims.internal.IImsRcsFeature;
+import com.android.ims.internal.IImsRegistration;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telecom.ITelecomService;
@@ -831,6 +832,17 @@
"android.telephony.event.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE";
/**
+ * {@link android.telecom.Connection} event used to indicate that an IMS call has be
+ * successfully handed over from LTE to WIFI.
+ * <p>
+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+ * The {@link Bundle} parameter is expected to be null when this connection event is used.
+ * @hide
+ */
+ public static final String EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI =
+ "android.telephony.event.EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI";
+
+ /**
* {@link android.telecom.Connection} event used to indicate that an IMS call failed to be
* handed over from LTE to WIFI.
* <p>
@@ -2503,6 +2515,33 @@
}
}
+ /**
+ * Resets the Carrier Keys in the database. This involves 2 steps:
+ * 1. Delete the keys from the database.
+ * 2. Send an intent to download new Certificates.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * @hide
+ */
+ public void resetCarrierKeysForImsiEncryption() {
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null) {
+ throw new RuntimeException("IMSI error: Subscriber Info is null");
+ }
+ int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
+ info.resetCarrierKeysForImsiEncryption(subId, mContext.getOpPackageName());
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getCarrierInfoForImsiEncryption RemoteException" + ex);
+ throw new RuntimeException("IMSI error: Remote Exception");
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ Rlog.e(TAG, "getCarrierInfoForImsiEncryption NullPointerException" + ex);
+ throw new RuntimeException("IMSI error: Null Pointer exception");
+ }
+ }
+
/**
* @param keyAvailability bitmask that defines the availabilty of keys for a type.
* @param keyType the key type which is being checked. (WLAN, EPDG)
@@ -2538,7 +2577,7 @@
* device keystore.
* <p>
* Requires Permission:
- * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
* @param imsiEncryptionInfo which includes the Key Type, the Public Key
* (java.security.PublicKey) and the Key Identifier.and the Key Identifier.
* The keyIdentifier Attribute value pair that helps a server locate
@@ -4921,6 +4960,25 @@
}
/**
+ * @return the {@IImsRegistration} interface that corresponds with the slot index and feature.
+ * @param slotIndex The SIM slot corresponding to the ImsService ImsRegistration is active for.
+ * @param feature An integer indicating the feature that we wish to get the ImsRegistration for.
+ * Corresponds to features defined in ImsFeature.
+ * @hide
+ */
+ public @Nullable IImsRegistration getImsRegistration(int slotIndex, int feature) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getImsRegistration(slotIndex, feature);
+ }
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "getImsRegistration, RemoteException: " + e.getMessage());
+ }
+ return null;
+ }
+
+ /**
* Set IMS registration state
*
* @param Registration state
@@ -6253,8 +6311,10 @@
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
*
- * @hide
+ * {@hide}
**/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void setSimPowerState(int state) {
setSimPowerStateForSlot(getSlotIndex(), state);
}
@@ -6273,8 +6333,10 @@
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
*
- * @hide
+ * {@hide}
**/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void setSimPowerStateForSlot(int slotIndex, int state) {
try {
ITelephony telephony = getITelephony();
diff --git a/telephony/java/android/telephony/VisualVoicemailSmsFilterSettings.java b/telephony/java/android/telephony/VisualVoicemailSmsFilterSettings.java
index 8ed96a3..7eeb1ce 100644
--- a/telephony/java/android/telephony/VisualVoicemailSmsFilterSettings.java
+++ b/telephony/java/android/telephony/VisualVoicemailSmsFilterSettings.java
@@ -15,12 +15,10 @@
*/
package android.telephony;
-import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
-
-import android.telecom.PhoneAccountHandle;
import android.telephony.VisualVoicemailService.VisualVoicemailTask;
+
import java.util.Collections;
import java.util.List;
@@ -75,6 +73,7 @@
private String mClientPrefix = DEFAULT_CLIENT_PREFIX;
private List<String> mOriginatingNumbers = DEFAULT_ORIGINATING_NUMBERS;
private int mDestinationPort = DEFAULT_DESTINATION_PORT;
+ private String mPackageName;
public VisualVoicemailSmsFilterSettings build() {
return new VisualVoicemailSmsFilterSettings(this);
@@ -116,6 +115,15 @@
return this;
}
+ /**
+ * The package that registered this filter.
+ *
+ * @hide
+ */
+ public Builder setPackageName(String packageName) {
+ mPackageName = packageName;
+ return this;
+ }
}
/**
@@ -138,12 +146,20 @@
public final int destinationPort;
/**
+ * The package that registered this filter.
+ *
+ * @hide
+ */
+ public final String packageName;
+
+ /**
* Use {@link Builder} to construct
*/
private VisualVoicemailSmsFilterSettings(Builder builder) {
clientPrefix = builder.mClientPrefix;
originatingNumbers = builder.mOriginatingNumbers;
destinationPort = builder.mDestinationPort;
+ packageName = builder.mPackageName;
}
public static final Creator<VisualVoicemailSmsFilterSettings> CREATOR =
@@ -154,7 +170,7 @@
builder.setClientPrefix(in.readString());
builder.setOriginatingNumbers(in.createStringArrayList());
builder.setDestinationPort(in.readInt());
-
+ builder.setPackageName(in.readString());
return builder.build();
}
@@ -174,10 +190,11 @@
dest.writeString(clientPrefix);
dest.writeStringList(originatingNumbers);
dest.writeInt(destinationPort);
+ dest.writeString(packageName);
}
@Override
- public String toString(){
+ public String toString() {
return "[VisualVoicemailSmsFilterSettings "
+ "clientPrefix=" + clientPrefix
+ ", originatingNumbers=" + originatingNumbers
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/data/ApnSetting.aidl
similarity index 86%
rename from telephony/java/android/telephony/data/InterfaceAddress.aidl
rename to telephony/java/android/telephony/data/ApnSetting.aidl
index d750363..381e5e8 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/android/telephony/data/ApnSetting.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-/** @hide */
package android.telephony.data;
-parcelable InterfaceAddress;
+parcelable ApnSetting;
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
new file mode 100644
index 0000000..2ab8d4f
--- /dev/null
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -0,0 +1,1370 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.data;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.StringDef;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.hardware.radio.V1_0.ApnTypes;
+import android.net.NetworkUtils;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.Telephony;
+import android.telephony.Rlog;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.MalformedURLException;
+import java.net.UnknownHostException;
+import java.net.URL;
+import java.net.InetAddress;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A class representing an APN configuration.
+ */
+public class ApnSetting implements Parcelable {
+
+ static final String LOG_TAG = "ApnSetting";
+ private static final boolean VDBG = false;
+
+ private final String mEntryName;
+ private final String mApnName;
+ private final InetAddress mProxy;
+ private final int mPort;
+ private final URL mMmsc;
+ private final InetAddress mMmsProxy;
+ private final int mMmsPort;
+ private final String mUser;
+ private final String mPassword;
+ private final int mAuthType;
+ private final List<String> mTypes;
+ private final int mTypesBitmap;
+ private final int mId;
+ private final String mOperatorNumeric;
+ private final String mProtocol;
+ private final String mRoamingProtocol;
+ private final int mMtu;
+
+ private final boolean mCarrierEnabled;
+ private final int mBearer;
+ private final int mBearerBitmask;
+
+ private final int mProfileId;
+
+ private final boolean mModemCognitive;
+ private final int mMaxConns;
+ private final int mWaitTime;
+ private final int mMaxConnsTime;
+
+ private final String mMvnoType;
+ private final String mMvnoMatchData;
+
+ private boolean mPermanentFailed = false;
+
+ /**
+ * Returns the types bitmap of the APN.
+ *
+ * @return types bitmap of the APN
+ * @hide
+ */
+ public int getTypesBitmap() {
+ return mTypesBitmap;
+ }
+
+ /**
+ * Returns the MTU size of the mobile interface to which the APN connected.
+ *
+ * @return the MTU size of the APN
+ * @hide
+ */
+ public int getMtu() {
+ return mMtu;
+ }
+
+ /**
+ * Radio Access Technology info.
+ * To check what values can hold, refer to ServiceState.java.
+ * This should be spread to other technologies,
+ * but currently only used for LTE(14) and EHRPD(13).
+ *
+ * @return the bearer info of the APN
+ * @hide
+ */
+ public int getBearer() {
+ return mBearer;
+ }
+
+ /**
+ * Returns the radio access technology bitmask for this APN.
+ *
+ * To check what values can hold, refer to ServiceState.java. This is a bitmask of radio
+ * technologies in ServiceState.
+ * This should be spread to other technologies,
+ * but currently only used for LTE(14) and EHRPD(13).
+ *
+ * @return the radio access technology bitmask
+ * @hide
+ */
+ public int getBearerBitmask() {
+ return mBearerBitmask;
+ }
+
+ /**
+ * Returns the profile id to which the APN saved in modem.
+ *
+ * @return the profile id of the APN
+ * @hide
+ */
+ public int getProfileId() {
+ return mProfileId;
+ }
+
+ /**
+ * Returns if the APN setting is to be set in modem.
+ *
+ * @return is the APN setting to be set in modem
+ * @hide
+ */
+ public boolean getModemCognitive() {
+ return mModemCognitive;
+ }
+
+ /**
+ * Returns the max connections of this APN.
+ *
+ * @return the max connections of this APN
+ * @hide
+ */
+ public int getMaxConns() {
+ return mMaxConns;
+ }
+
+ /**
+ * Returns the wait time for retry of the APN.
+ *
+ * @return the wait time for retry of the APN
+ * @hide
+ */
+ public int getWaitTime() {
+ return mWaitTime;
+ }
+
+ /**
+ * Returns the time to limit max connection for the APN.
+ *
+ * @return the time to limit max connection for the APN
+ * @hide
+ */
+ public int getMaxConnsTime() {
+ return mMaxConnsTime;
+ }
+
+ /**
+ * Returns the MVNO data. Examples:
+ * "spn": A MOBILE, BEN NL
+ * "imsi": 302720x94, 2060188
+ * "gid": 4E, 33
+ * "iccid": 898603 etc..
+ *
+ * @return the mvno match data
+ * @hide
+ */
+ public String getMvnoMatchData() {
+ return mMvnoMatchData;
+ }
+
+ /**
+ * Indicates this APN setting is permanently failed and cannot be
+ * retried by the retry manager anymore.
+ *
+ * @return if this APN setting is permanently failed
+ * @hide
+ */
+ public boolean getPermanentFailed() {
+ return mPermanentFailed;
+ }
+
+ /**
+ * Sets if this APN setting is permanently failed.
+ *
+ * @param permanentFailed if this APN setting is permanently failed
+ * @hide
+ */
+ public void setPermanentFailed(boolean permanentFailed) {
+ mPermanentFailed = permanentFailed;
+ }
+
+ /**
+ * Returns the entry name of the APN.
+ *
+ * @return the entry name for the APN
+ */
+ public String getEntryName() {
+ return mEntryName;
+ }
+
+ /**
+ * Returns the name of the APN.
+ *
+ * @return APN name
+ */
+ public String getApnName() {
+ return mApnName;
+ }
+
+ /**
+ * Returns the proxy address of the APN.
+ *
+ * @return proxy address.
+ */
+ public InetAddress getProxy() {
+ return mProxy;
+ }
+
+ /**
+ * Returns the proxy port of the APN.
+ *
+ * @return proxy port
+ */
+ public int getPort() {
+ return mPort;
+ }
+ /**
+ * Returns the MMSC URL of the APN.
+ *
+ * @return MMSC URL.
+ */
+ public URL getMmsc() {
+ return mMmsc;
+ }
+
+ /**
+ * Returns the MMS proxy address of the APN.
+ *
+ * @return MMS proxy address.
+ */
+ public InetAddress getMmsProxy() {
+ return mMmsProxy;
+ }
+
+ /**
+ * Returns the MMS proxy port of the APN.
+ *
+ * @return MMS proxy port
+ */
+ public int getMmsPort() {
+ return mMmsPort;
+ }
+
+ /**
+ * Returns the APN username of the APN.
+ *
+ * @return APN username
+ */
+ public String getUser() {
+ return mUser;
+ }
+
+ /**
+ * Returns the APN password of the APN.
+ *
+ * @return APN password
+ */
+ public String getPassword() {
+ return mPassword;
+ }
+
+ /** @hide */
+ @IntDef({
+ AUTH_TYPE_NONE,
+ AUTH_TYPE_PAP,
+ AUTH_TYPE_CHAP,
+ AUTH_TYPE_PAP_OR_CHAP,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AuthType {}
+
+ /**
+ * Returns the authentication type of the APN.
+ *
+ * Example of possible values: {@link #AUTH_TYPE_NONE}, {@link #AUTH_TYPE_PAP}.
+ *
+ * @return authentication type
+ */
+ @AuthType
+ public int getAuthType() {
+ return mAuthType;
+ }
+
+ /** @hide */
+ @StringDef({
+ TYPE_DEFAULT,
+ TYPE_MMS,
+ TYPE_SUPL,
+ TYPE_DUN,
+ TYPE_HIPRI,
+ TYPE_FOTA,
+ TYPE_IMS,
+ TYPE_CBS,
+ TYPE_IA,
+ TYPE_EMERGENCY
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ApnType {}
+
+ /**
+ * Returns the list of APN types of the APN.
+ *
+ * Example of possible values: {@link #TYPE_DEFAULT}, {@link #TYPE_MMS}.
+ *
+ * @return the list of APN types
+ */
+ @ApnType
+ public List<String> getTypes() {
+ return mTypes;
+ }
+
+ /**
+ * Returns the unique database id for this entry.
+ *
+ * @return the unique database id
+ */
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * Returns the numeric operator ID for the APN. Usually
+ * {@link android.provider.Telephony.Carriers#MCC} +
+ * {@link android.provider.Telephony.Carriers#MNC}.
+ *
+ * @return the numeric operator ID
+ */
+ public String getOperatorNumeric() {
+ return mOperatorNumeric;
+ }
+
+ /** @hide */
+ @StringDef({
+ PROTOCOL_IP,
+ PROTOCOL_IPV6,
+ PROTOCOL_IPV4V6,
+ PROTOCOL_PPP,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ProtocolType {}
+
+ /**
+ * Returns the protocol to use to connect to this APN.
+ *
+ * One of the {@code PDP_type} values in TS 27.007 section 10.1.1.
+ * Example of possible values: {@link #PROTOCOL_IP}, {@link #PROTOCOL_IPV6}.
+ *
+ * @return the protocol
+ */
+ @ProtocolType
+ public String getProtocol() {
+ return mProtocol;
+ }
+
+ /**
+ * Returns the protocol to use to connect to this APN when roaming.
+ *
+ * The syntax is the same as {@link android.provider.Telephony.Carriers#PROTOCOL}.
+ *
+ * @return the roaming protocol
+ */
+ public String getRoamingProtocol() {
+ return mRoamingProtocol;
+ }
+
+ /**
+ * Returns the current status of APN.
+ *
+ * {@code true} : enabled APN.
+ * {@code false} : disabled APN.
+ *
+ * @return the current status
+ */
+ public boolean isEnabled() {
+ return mCarrierEnabled;
+ }
+
+ /** @hide */
+ @StringDef({
+ MVNO_TYPE_SPN,
+ MVNO_TYPE_IMSI,
+ MVNO_TYPE_GID,
+ MVNO_TYPE_ICCID,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MvnoType {}
+
+ /**
+ * Returns the MVNO match type for this APN.
+ *
+ * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
+ *
+ * @return the MVNO match type
+ */
+ @MvnoType
+ public String getMvnoType() {
+ return mMvnoType;
+ }
+
+ private ApnSetting(Builder builder) {
+ this.mEntryName = builder.mEntryName;
+ this.mApnName = builder.mApnName;
+ this.mProxy = builder.mProxy;
+ this.mPort = builder.mPort;
+ this.mMmsc = builder.mMmsc;
+ this.mMmsProxy = builder.mMmsProxy;
+ this.mMmsPort = builder.mMmsPort;
+ this.mUser = builder.mUser;
+ this.mPassword = builder.mPassword;
+ this.mAuthType = builder.mAuthType;
+ this.mTypes = (builder.mTypes == null ? new ArrayList<String>() : builder.mTypes);
+ this.mTypesBitmap = builder.mTypesBitmap;
+ this.mId = builder.mId;
+ this.mOperatorNumeric = builder.mOperatorNumeric;
+ this.mProtocol = builder.mProtocol;
+ this.mRoamingProtocol = builder.mRoamingProtocol;
+ this.mMtu = builder.mMtu;
+ this.mCarrierEnabled = builder.mCarrierEnabled;
+ this.mBearer = builder.mBearer;
+ this.mBearerBitmask = builder.mBearerBitmask;
+ this.mProfileId = builder.mProfileId;
+ this.mModemCognitive = builder.mModemCognitive;
+ this.mMaxConns = builder.mMaxConns;
+ this.mWaitTime = builder.mWaitTime;
+ this.mMaxConnsTime = builder.mMaxConnsTime;
+ this.mMvnoType = builder.mMvnoType;
+ this.mMvnoMatchData = builder.mMvnoMatchData;
+ }
+
+ /** @hide */
+ public static ApnSetting makeApnSetting(int id, String operatorNumeric, String entryName,
+ String apnName, InetAddress proxy, int port, URL mmsc, InetAddress mmsProxy,
+ int mmsPort, String user, String password, int authType, List<String> types,
+ String protocol, String roamingProtocol, boolean carrierEnabled, int bearer,
+ int bearerBitmask, int profileId, boolean modemCognitive, int maxConns,
+ int waitTime, int maxConnsTime, int mtu, String mvnoType, String mvnoMatchData) {
+ return new Builder()
+ .setId(id)
+ .setOperatorNumeric(operatorNumeric)
+ .setEntryName(entryName)
+ .setApnName(apnName)
+ .setProxy(proxy)
+ .setPort(port)
+ .setMmsc(mmsc)
+ .setMmsProxy(mmsProxy)
+ .setMmsPort(mmsPort)
+ .setUser(user)
+ .setPassword(password)
+ .setAuthType(authType)
+ .setTypes(types)
+ .setProtocol(protocol)
+ .setRoamingProtocol(roamingProtocol)
+ .setCarrierEnabled(carrierEnabled)
+ .setBearer(bearer)
+ .setBearerBitmask(bearerBitmask)
+ .setProfileId(profileId)
+ .setModemCognitive(modemCognitive)
+ .setMaxConns(maxConns)
+ .setWaitTime(waitTime)
+ .setMaxConnsTime(maxConnsTime)
+ .setMtu(mtu)
+ .setMvnoType(mvnoType)
+ .setMvnoMatchData(mvnoMatchData)
+ .build();
+ }
+
+ /** @hide */
+ public static ApnSetting makeApnSetting(Cursor cursor) {
+ String[] types = parseTypes(
+ cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
+
+ return makeApnSetting(
+ cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)),
+ cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)),
+ cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)),
+ cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)),
+ inetAddressFromString(cursor.getString(
+ cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))),
+ portFromString(cursor.getString(
+ cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT))),
+ URLFromString(cursor.getString(
+ cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))),
+ inetAddressFromString(cursor.getString(
+ cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))),
+ portFromString(cursor.getString(
+ cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT))),
+ cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
+ cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
+ cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
+ Arrays.asList(types),
+ cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)),
+ cursor.getString(cursor.getColumnIndexOrThrow(
+ Telephony.Carriers.ROAMING_PROTOCOL)),
+ cursor.getInt(cursor.getColumnIndexOrThrow(
+ Telephony.Carriers.CARRIER_ENABLED)) == 1,
+ cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER)),
+ cursor.getInt(cursor.getColumnIndexOrThrow(
+ Telephony.Carriers.BEARER_BITMASK)),
+ cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID)),
+ cursor.getInt(cursor.getColumnIndexOrThrow(
+ Telephony.Carriers.MODEM_COGNITIVE)) == 1,
+ cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNS)),
+ cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.WAIT_TIME)),
+ cursor.getInt(cursor.getColumnIndexOrThrow(
+ Telephony.Carriers.MAX_CONNS_TIME)),
+ cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)),
+ cursor.getString(cursor.getColumnIndexOrThrow(
+ Telephony.Carriers.MVNO_TYPE)),
+ cursor.getString(cursor.getColumnIndexOrThrow(
+ Telephony.Carriers.MVNO_MATCH_DATA)));
+ }
+
+ /** @hide */
+ public static ApnSetting makeApnSetting(ApnSetting apn) {
+ return makeApnSetting(apn.mId, apn.mOperatorNumeric, apn.mEntryName, apn.mApnName,
+ apn.mProxy, apn.mPort, apn.mMmsc, apn.mMmsProxy, apn.mMmsPort, apn.mUser,
+ apn.mPassword, apn.mAuthType, apn.mTypes, apn.mProtocol, apn.mRoamingProtocol,
+ apn.mCarrierEnabled, apn.mBearer, apn.mBearerBitmask, apn.mProfileId,
+ apn.mModemCognitive, apn.mMaxConns, apn.mWaitTime, apn.mMaxConnsTime, apn.mMtu,
+ apn.mMvnoType, apn.mMvnoMatchData);
+ }
+
+ /** @hide */
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[ApnSettingV3] ")
+ .append(mEntryName)
+ .append(", ").append(mId)
+ .append(", ").append(mOperatorNumeric)
+ .append(", ").append(mApnName)
+ .append(", ").append(inetAddressToString(mProxy))
+ .append(", ").append(URLToString(mMmsc))
+ .append(", ").append(inetAddressToString(mMmsProxy))
+ .append(", ").append(portToString(mMmsPort))
+ .append(", ").append(portToString(mPort))
+ .append(", ").append(mAuthType).append(", ");
+ for (int i = 0; i < mTypes.size(); i++) {
+ sb.append(mTypes.get(i));
+ if (i < mTypes.size() - 1) {
+ sb.append(" | ");
+ }
+ }
+ sb.append(", ").append(mProtocol);
+ sb.append(", ").append(mRoamingProtocol);
+ sb.append(", ").append(mCarrierEnabled);
+ sb.append(", ").append(mBearer);
+ sb.append(", ").append(mBearerBitmask);
+ sb.append(", ").append(mProfileId);
+ sb.append(", ").append(mModemCognitive);
+ sb.append(", ").append(mMaxConns);
+ sb.append(", ").append(mWaitTime);
+ sb.append(", ").append(mMaxConnsTime);
+ sb.append(", ").append(mMtu);
+ sb.append(", ").append(mMvnoType);
+ sb.append(", ").append(mMvnoMatchData);
+ sb.append(", ").append(mPermanentFailed);
+ return sb.toString();
+ }
+
+ /**
+ * Returns true if there are MVNO params specified.
+ * @hide
+ */
+ public boolean hasMvnoParams() {
+ return !TextUtils.isEmpty(mMvnoType) && !TextUtils.isEmpty(mMvnoMatchData);
+ }
+
+ /** @hide */
+ public boolean canHandleType(String type) {
+ if (!mCarrierEnabled) return false;
+ boolean wildcardable = true;
+ if (TYPE_IA.equalsIgnoreCase(type)) wildcardable = false;
+ for (String t : mTypes) {
+ // DEFAULT handles all, and HIPRI is handled by DEFAULT
+ if (t.equalsIgnoreCase(type)
+ || (wildcardable && t.equalsIgnoreCase(TYPE_ALL))
+ || (t.equalsIgnoreCase(TYPE_DEFAULT)
+ && type.equalsIgnoreCase(TYPE_HIPRI))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // check whether the types of two APN same (even only one type of each APN is same)
+ private boolean typeSameAny(ApnSetting first, ApnSetting second) {
+ if (VDBG) {
+ StringBuilder apnType1 = new StringBuilder(first.mApnName + ": ");
+ for (int index1 = 0; index1 < first.mTypes.size(); index1++) {
+ apnType1.append(first.mTypes.get(index1));
+ apnType1.append(",");
+ }
+
+ StringBuilder apnType2 = new StringBuilder(second.mApnName + ": ");
+ for (int index1 = 0; index1 < second.mTypes.size(); index1++) {
+ apnType2.append(second.mTypes.get(index1));
+ apnType2.append(",");
+ }
+ Rlog.d(LOG_TAG, "APN1: is " + apnType1);
+ Rlog.d(LOG_TAG, "APN2: is " + apnType2);
+ }
+
+ for (int index1 = 0; index1 < first.mTypes.size(); index1++) {
+ for (int index2 = 0; index2 < second.mTypes.size(); index2++) {
+ if (first.mTypes.get(index1).equals(ApnSetting.TYPE_ALL)
+ || second.mTypes.get(index2).equals(ApnSetting.TYPE_ALL)
+ || first.mTypes.get(index1).equals(second.mTypes.get(index2))) {
+ if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return true");
+ return true;
+ }
+ }
+ }
+
+ if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return false");
+ return false;
+ }
+
+ // TODO - if we have this function we should also have hashCode.
+ // Also should handle changes in type order and perhaps case-insensitivity
+ /** @hide */
+ public boolean equals(Object o) {
+ if (o instanceof ApnSetting == false) {
+ return false;
+ }
+
+ ApnSetting other = (ApnSetting) o;
+
+ return mEntryName.equals(other.mEntryName)
+ && Objects.equals(mId, other.mId)
+ && Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
+ && Objects.equals(mApnName, other.mApnName)
+ && Objects.equals(mProxy, other.mProxy)
+ && Objects.equals(mMmsc, other.mMmsc)
+ && Objects.equals(mMmsProxy, other.mMmsProxy)
+ && Objects.equals(mMmsPort, other.mMmsPort)
+ && Objects.equals(mPort,other.mPort)
+ && Objects.equals(mUser, other.mUser)
+ && Objects.equals(mPassword, other.mPassword)
+ && Objects.equals(mAuthType, other.mAuthType)
+ && Objects.equals(mTypes, other.mTypes)
+ && Objects.equals(mTypesBitmap, other.mTypesBitmap)
+ && Objects.equals(mProtocol, other.mProtocol)
+ && Objects.equals(mRoamingProtocol, other.mRoamingProtocol)
+ && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
+ && Objects.equals(mBearer, other.mBearer)
+ && Objects.equals(mBearerBitmask, other.mBearerBitmask)
+ && Objects.equals(mProfileId, other.mProfileId)
+ && Objects.equals(mModemCognitive, other.mModemCognitive)
+ && Objects.equals(mMaxConns, other.mMaxConns)
+ && Objects.equals(mWaitTime, other.mWaitTime)
+ && Objects.equals(mMaxConnsTime, other.mMaxConnsTime)
+ && Objects.equals(mMtu, other.mMtu)
+ && Objects.equals(mMvnoType, other.mMvnoType)
+ && Objects.equals(mMvnoMatchData, other.mMvnoMatchData);
+ }
+
+ /**
+ * Compare two APN settings
+ *
+ * Note: This method does not compare 'id', 'bearer', 'bearerBitmask'. We only use this for
+ * determining if tearing a data call is needed when conditions change. See
+ * cleanUpConnectionsOnUpdatedApns in DcTracker.
+ *
+ * @param o the other object to compare
+ * @param isDataRoaming True if the device is on data roaming
+ * @return True if the two APN settings are same
+ * @hide
+ */
+ public boolean equals(Object o, boolean isDataRoaming) {
+ if (!(o instanceof ApnSetting)) {
+ return false;
+ }
+
+ ApnSetting other = (ApnSetting) o;
+
+ return mEntryName.equals(other.mEntryName)
+ && Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
+ && Objects.equals(mApnName, other.mApnName)
+ && Objects.equals(mProxy, other.mProxy)
+ && Objects.equals(mMmsc, other.mMmsc)
+ && Objects.equals(mMmsProxy, other.mMmsProxy)
+ && Objects.equals(mMmsPort, other.mMmsPort)
+ && Objects.equals(mPort, other.mPort)
+ && Objects.equals(mUser, other.mUser)
+ && Objects.equals(mPassword, other.mPassword)
+ && Objects.equals(mAuthType, other.mAuthType)
+ && Objects.equals(mTypes, other.mTypes)
+ && Objects.equals(mTypesBitmap, other.mTypesBitmap)
+ && (isDataRoaming || Objects.equals(mProtocol,other.mProtocol))
+ && (!isDataRoaming || Objects.equals(mRoamingProtocol, other.mRoamingProtocol))
+ && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
+ && Objects.equals(mProfileId, other.mProfileId)
+ && Objects.equals(mModemCognitive, other.mModemCognitive)
+ && Objects.equals(mMaxConns, other.mMaxConns)
+ && Objects.equals(mWaitTime, other.mWaitTime)
+ && Objects.equals(mMaxConnsTime, other.mMaxConnsTime)
+ && Objects.equals(mMtu, other.mMtu)
+ && Objects.equals(mMvnoType, other.mMvnoType)
+ && Objects.equals(mMvnoMatchData, other.mMvnoMatchData);
+ }
+
+ /**
+ * Check if neither mention DUN and are substantially similar
+ *
+ * @param other The other APN settings to compare
+ * @return True if two APN settings are similar
+ * @hide
+ */
+ public boolean similar(ApnSetting other) {
+ return (!this.canHandleType(TYPE_DUN)
+ && !other.canHandleType(TYPE_DUN)
+ && Objects.equals(this.mApnName, other.mApnName)
+ && !typeSameAny(this, other)
+ && xorEqualsInetAddress(this.mProxy, other.mProxy)
+ && xorEqualsPort(this.mPort, other.mPort)
+ && xorEquals(this.mProtocol, other.mProtocol)
+ && xorEquals(this.mRoamingProtocol, other.mRoamingProtocol)
+ && Objects.equals(this.mCarrierEnabled, other.mCarrierEnabled)
+ && Objects.equals(this.mBearerBitmask, other.mBearerBitmask)
+ && Objects.equals(this.mProfileId, other.mProfileId)
+ && Objects.equals(this.mMvnoType, other.mMvnoType)
+ && Objects.equals(this.mMvnoMatchData, other.mMvnoMatchData)
+ && xorEqualsURL(this.mMmsc, other.mMmsc)
+ && xorEqualsInetAddress(this.mMmsProxy, other.mMmsProxy)
+ && xorEqualsPort(this.mMmsPort, other.mMmsPort));
+ }
+
+ // Equal or one is not specified.
+ private boolean xorEquals(String first, String second) {
+ return (Objects.equals(first, second)
+ || TextUtils.isEmpty(first)
+ || TextUtils.isEmpty(second));
+ }
+
+ // Equal or one is not specified.
+ private boolean xorEqualsInetAddress(InetAddress first, InetAddress second) {
+ return first == null || second == null || first.equals(second);
+ }
+
+ // Equal or one is not specified.
+ private boolean xorEqualsURL(URL first, URL second) {
+ return first == null || second == null || first.equals(second);
+ }
+
+ // Equal or one is not specified.
+ private boolean xorEqualsPort(int first, int second) {
+ return first == -1 || second == -1 || Objects.equals(first, second);
+ }
+
+ // Helper function to convert APN string into a 32-bit bitmask.
+ private static int getApnBitmask(String apn) {
+ switch (apn) {
+ case TYPE_DEFAULT: return ApnTypes.DEFAULT;
+ case TYPE_MMS: return ApnTypes.MMS;
+ case TYPE_SUPL: return ApnTypes.SUPL;
+ case TYPE_DUN: return ApnTypes.DUN;
+ case TYPE_HIPRI: return ApnTypes.HIPRI;
+ case TYPE_FOTA: return ApnTypes.FOTA;
+ case TYPE_IMS: return ApnTypes.IMS;
+ case TYPE_CBS: return ApnTypes.CBS;
+ case TYPE_IA: return ApnTypes.IA;
+ case TYPE_EMERGENCY: return ApnTypes.EMERGENCY;
+ case TYPE_ALL: return ApnTypes.ALL;
+ default: return ApnTypes.NONE;
+ }
+ }
+
+ private String deParseTypes(List<String> types) {
+ if (types == null) {
+ return null;
+ }
+ return TextUtils.join(",", types);
+ }
+
+ /** @hide */
+ // Called by DPM.
+ public ContentValues toContentValues() {
+ ContentValues apnValue = new ContentValues();
+ if (mOperatorNumeric != null) {
+ apnValue.put(Telephony.Carriers.NUMERIC, mOperatorNumeric);
+ }
+ if (mEntryName != null) {
+ apnValue.put(Telephony.Carriers.NAME, mEntryName);
+ }
+ if (mApnName != null) {
+ apnValue.put(Telephony.Carriers.APN, mApnName);
+ }
+ if (mProxy != null) {
+ apnValue.put(Telephony.Carriers.PROXY, inetAddressToString(mProxy));
+ }
+ apnValue.put(Telephony.Carriers.PORT, portToString(mPort));
+ if (mMmsc != null) {
+ apnValue.put(Telephony.Carriers.MMSC, URLToString(mMmsc));
+ }
+ apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsPort));
+ if (mMmsProxy != null) {
+ apnValue.put(Telephony.Carriers.MMSPROXY, inetAddressToString(mMmsProxy));
+ }
+ if (mUser != null) {
+ apnValue.put(Telephony.Carriers.USER, mUser);
+ }
+ if (mPassword != null) {
+ apnValue.put(Telephony.Carriers.PASSWORD, mPassword);
+ }
+ apnValue.put(Telephony.Carriers.AUTH_TYPE, mAuthType);
+ String apnType = deParseTypes(mTypes);
+ if (apnType != null) {
+ apnValue.put(Telephony.Carriers.TYPE, apnType);
+ }
+ if (mProtocol != null) {
+ apnValue.put(Telephony.Carriers.PROTOCOL, mProtocol);
+ }
+ if (mRoamingProtocol != null) {
+ apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL, mRoamingProtocol);
+ }
+ apnValue.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled);
+ // networkTypeBit.
+ apnValue.put(Telephony.Carriers.BEARER_BITMASK, mBearerBitmask);
+ if (mMvnoType != null) {
+ apnValue.put(Telephony.Carriers.MVNO_TYPE, mMvnoType);
+ }
+
+ return apnValue;
+ }
+
+ /**
+ * @param types comma delimited list of APN types
+ * @return array of APN types
+ * @hide
+ */
+ public static String[] parseTypes(String types) {
+ String[] result;
+ // If unset, set to DEFAULT.
+ if (TextUtils.isEmpty(types)) {
+ result = new String[1];
+ result[0] = TYPE_ALL;
+ } else {
+ result = types.split(",");
+ }
+ return result;
+ }
+
+ private static URL URLFromString(String url) {
+ try {
+ return TextUtils.isEmpty(url) ? null : new URL(url);
+ } catch (MalformedURLException e) {
+ Log.e(LOG_TAG, "Can't parse URL from string.");
+ return null;
+ }
+ }
+
+ private static String URLToString(URL url) {
+ return url == null ? "" : url.toString();
+ }
+
+ private static InetAddress inetAddressFromString(String inetAddress) {
+ if (TextUtils.isEmpty(inetAddress)) {
+ return null;
+ }
+ try {
+ return InetAddress.getByName(inetAddress);
+ } catch (UnknownHostException e) {
+ Log.e(LOG_TAG, "Can't parse InetAddress from string: unknown host.");
+ return null;
+ }
+ }
+
+ private static String inetAddressToString(InetAddress inetAddress) {
+ if (inetAddress == null) {
+ return null;
+ }
+ return TextUtils.isEmpty(inetAddress.getHostName())
+ ? inetAddress.getHostAddress() : inetAddress.getHostName();
+ }
+
+ private static int portFromString(String strPort) {
+ int port = -1;
+ if (!TextUtils.isEmpty(strPort)) {
+ try {
+ port = Integer.parseInt(strPort);
+ } catch (NumberFormatException e) {
+ Log.e(LOG_TAG, "Can't parse port from String");
+ }
+ }
+ return port;
+ }
+
+ private static String portToString(int port) {
+ return port == -1 ? "" : Integer.toString(port);
+ }
+
+ // Implement Parcelable.
+ @Override
+ /** @hide */
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ /** @hide */
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mId);
+ dest.writeString(mOperatorNumeric);
+ dest.writeString(mEntryName);
+ dest.writeString(mApnName);
+ dest.writeValue(mProxy);
+ dest.writeInt(mPort);
+ dest.writeValue(mMmsc);
+ dest.writeValue(mMmsProxy);
+ dest.writeInt(mMmsPort);
+ dest.writeString(mUser);
+ dest.writeString(mPassword);
+ dest.writeInt(mAuthType);
+ dest.writeStringArray(mTypes.toArray(new String[0]));
+ dest.writeString(mProtocol);
+ dest.writeString(mRoamingProtocol);
+ dest.writeInt(mCarrierEnabled ? 1: 0);
+ dest.writeString(mMvnoType);
+ }
+
+ private static ApnSetting readFromParcel(Parcel in) {
+ return makeApnSetting(in.readInt(), in.readString(), in.readString(), in.readString(),
+ (InetAddress)in.readValue(InetAddress.class.getClassLoader()),
+ in.readInt(), (URL)in.readValue(URL.class.getClassLoader()),
+ (InetAddress)in.readValue(InetAddress.class.getClassLoader()),
+ in.readInt(), in.readString(), in.readString(), in.readInt(),
+ Arrays.asList(in.readStringArray()), in.readString(), in.readString(),
+ in.readInt() > 0, 0, 0, 0, false, 0, 0, 0, 0, in.readString(), null);
+ }
+
+ public static final Parcelable.Creator<ApnSetting> CREATOR =
+ new Parcelable.Creator<ApnSetting>() {
+ @Override
+ public ApnSetting createFromParcel(Parcel in) {
+ return readFromParcel(in);
+ }
+
+ @Override
+ public ApnSetting[] newArray(int size) {
+ return new ApnSetting[size];
+ }
+ };
+
+ /**
+ * APN types for data connections. These are usage categories for an APN
+ * entry. One APN entry may support multiple APN types, eg, a single APN
+ * may service regular internet traffic ("default") as well as MMS-specific
+ * connections.<br/>
+ * ALL is a special type to indicate that this APN entry can
+ * service all data connections.
+ */
+ public static final String TYPE_ALL = "*";
+ /** APN type for default data traffic */
+ public static final String TYPE_DEFAULT = "default";
+ /** APN type for MMS traffic */
+ public static final String TYPE_MMS = "mms";
+ /** APN type for SUPL assisted GPS */
+ public static final String TYPE_SUPL = "supl";
+ /** APN type for DUN traffic */
+ public static final String TYPE_DUN = "dun";
+ /** APN type for HiPri traffic */
+ public static final String TYPE_HIPRI = "hipri";
+ /** APN type for FOTA */
+ public static final String TYPE_FOTA = "fota";
+ /** APN type for IMS */
+ public static final String TYPE_IMS = "ims";
+ /** APN type for CBS */
+ public static final String TYPE_CBS = "cbs";
+ /** APN type for IA Initial Attach APN */
+ public static final String TYPE_IA = "ia";
+ /** APN type for Emergency PDN. This is not an IA apn, but is used
+ * for access to carrier services in an emergency call situation. */
+ public static final String TYPE_EMERGENCY = "emergency";
+ /**
+ * Array of all APN types
+ *
+ * @hide
+ */
+ public static final String[] ALL_TYPES = {
+ TYPE_DEFAULT,
+ TYPE_MMS,
+ TYPE_SUPL,
+ TYPE_DUN,
+ TYPE_HIPRI,
+ TYPE_FOTA,
+ TYPE_IMS,
+ TYPE_CBS,
+ TYPE_IA,
+ TYPE_EMERGENCY
+ };
+
+ // Possible values for authentication types.
+ public static final int AUTH_TYPE_NONE = 0;
+ public static final int AUTH_TYPE_PAP = 1;
+ public static final int AUTH_TYPE_CHAP = 2;
+ public static final int AUTH_TYPE_PAP_OR_CHAP = 3;
+
+ // Possible values for protocol.
+ public static final String PROTOCOL_IP = "IP";
+ public static final String PROTOCOL_IPV6 = "IPV6";
+ public static final String PROTOCOL_IPV4V6 = "IPV4V6";
+ public static final String PROTOCOL_PPP = "PPP";
+
+ // Possible values for MVNO type.
+ public static final String MVNO_TYPE_SPN = "spn";
+ public static final String MVNO_TYPE_IMSI = "imsi";
+ public static final String MVNO_TYPE_GID = "gid";
+ public static final String MVNO_TYPE_ICCID = "iccid";
+
+ public static class Builder{
+ private String mEntryName;
+ private String mApnName;
+ private InetAddress mProxy;
+ private int mPort = -1;
+ private URL mMmsc;
+ private InetAddress mMmsProxy;
+ private int mMmsPort = -1;
+ private String mUser;
+ private String mPassword;
+ private int mAuthType;
+ private List<String> mTypes;
+ private int mTypesBitmap;
+ private int mId;
+ private String mOperatorNumeric;
+ private String mProtocol;
+ private String mRoamingProtocol;
+ private int mMtu;
+ private boolean mCarrierEnabled;
+ private int mBearer;
+ private int mBearerBitmask;
+ private int mProfileId;
+ private boolean mModemCognitive;
+ private int mMaxConns;
+ private int mWaitTime;
+ private int mMaxConnsTime;
+ private String mMvnoType;
+ private String mMvnoMatchData;
+
+ /**
+ * Default constructor for Builder.
+ */
+ public Builder() {}
+
+ /**
+ * Set the MTU size of the mobile interface to which the APN connected.
+ *
+ * @param mtu the MTU size to set for the APN
+ * @hide
+ */
+ public Builder setMtu(int mtu) {
+ this.mMtu = mtu;
+ return this;
+ }
+
+ /**
+ * Sets bearer info.
+ *
+ * @param bearer the bearer info to set for the APN
+ * @hide
+ */
+ public Builder setBearer(int bearer) {
+ this.mBearer = bearer;
+ return this;
+ }
+
+ /**
+ * Sets the radio access technology bitmask for this APN.
+ *
+ * @param bearerBitmask the radio access technology bitmask to set for this APN
+ * @hide
+ */
+ public Builder setBearerBitmask(int bearerBitmask) {
+ this.mBearerBitmask = bearerBitmask;
+ return this;
+ }
+
+ /**
+ * Sets the profile id to which the APN saved in modem.
+ *
+ * @param profileId the profile id to set for the APN
+ * @hide
+ */
+ public Builder setProfileId(int profileId) {
+ this.mProfileId = profileId;
+ return this;
+ }
+
+ /**
+ * Sets if the APN setting is to be set in modem.
+ *
+ * @param modemCognitive if the APN setting is to be set in modem
+ * @hide
+ */
+ public Builder setModemCognitive(boolean modemCognitive) {
+ this.mModemCognitive = modemCognitive;
+ return this;
+ }
+
+ /**
+ * Sets the max connections of this APN.
+ *
+ * @param maxConns the max connections of this APN
+ * @hide
+ */
+ public Builder setMaxConns(int maxConns) {
+ this.mMaxConns = maxConns;
+ return this;
+ }
+
+ /**
+ * Sets the wait time for retry of the APN.
+ *
+ * @param waitTime the wait time for retry of the APN
+ * @hide
+ */
+ public Builder setWaitTime(int waitTime) {
+ this.mWaitTime = waitTime;
+ return this;
+ }
+
+ /**
+ * Sets the time to limit max connection for the APN.
+ *
+ * @param maxConnsTime the time to limit max connection for the APN
+ * @hide
+ */
+ public Builder setMaxConnsTime(int maxConnsTime) {
+ this.mMaxConnsTime = maxConnsTime;
+ return this;
+ }
+
+ /**
+ * Sets the MVNO match data for the APN.
+ *
+ * @param mvnoMatchData the MVNO match data for the APN
+ * @hide
+ */
+ public Builder setMvnoMatchData(String mvnoMatchData) {
+ this.mMvnoMatchData = mvnoMatchData;
+ return this;
+ }
+
+ /**
+ * Sets the entry name of the APN.
+ *
+ * @param entryName the entry name to set for the APN
+ */
+ public Builder setEntryName(String entryName) {
+ this.mEntryName = entryName;
+ return this;
+ }
+
+ /**
+ * Sets the name of the APN.
+ *
+ * @param apnName the name to set for the APN
+ */
+ public Builder setApnName(String apnName) {
+ this.mApnName = apnName;
+ return this;
+ }
+
+ /**
+ * Sets the proxy address of the APN.
+ *
+ * @param proxy the proxy address to set for the APN
+ */
+ public Builder setProxy(InetAddress proxy) {
+ this.mProxy = proxy;
+ return this;
+ }
+
+ /**
+ * Sets the proxy port of the APN.
+ *
+ * @param port the proxy port to set for the APN
+ */
+ public Builder setPort(int port) {
+ this.mPort = port;
+ return this;
+ }
+
+ /**
+ * Sets the MMSC URL of the APN.
+ *
+ * @param mmsc the MMSC URL to set for the APN
+ */
+ public Builder setMmsc(URL mmsc) {
+ this.mMmsc = mmsc;
+ return this;
+ }
+
+ /**
+ * Sets the MMS proxy address of the APN.
+ *
+ * @param mmsProxy the MMS proxy address to set for the APN
+ */
+ public Builder setMmsProxy(InetAddress mmsProxy) {
+ this.mMmsProxy = mmsProxy;
+ return this;
+ }
+
+ /**
+ * Sets the MMS proxy port of the APN.
+ *
+ * @param mmsPort the MMS proxy port to set for the APN
+ */
+ public Builder setMmsPort(int mmsPort) {
+ this.mMmsPort = mmsPort;
+ return this;
+ }
+
+ /**
+ * Sets the APN username of the APN.
+ *
+ * @param user the APN username to set for the APN
+ */
+ public Builder setUser(String user) {
+ this.mUser = user;
+ return this;
+ }
+
+ /**
+ * Sets the APN password of the APN.
+ *
+ * @see android.provider.Telephony.Carriers#PASSWORD
+ * @param password the APN password to set for the APN
+ */
+ public Builder setPassword(String password) {
+ this.mPassword = password;
+ return this;
+ }
+
+ /**
+ * Sets the authentication type of the APN.
+ *
+ * Example of possible values: {@link #AUTH_TYPE_NONE}, {@link #AUTH_TYPE_PAP}.
+ *
+ * @param authType the authentication type to set for the APN
+ */
+ public Builder setAuthType(@AuthType int authType) {
+ this.mAuthType = authType;
+ return this;
+ }
+
+ /**
+ * Sets the list of APN types of the APN.
+ *
+ * Example of possible values: {@link #TYPE_DEFAULT}, {@link #TYPE_MMS}.
+ *
+ * @param types the list of APN types to set for the APN
+ */
+ public Builder setTypes(@ApnType List<String> types) {
+ this.mTypes = types;
+ int apnBitmap = 0;
+ for (int i = 0; i < mTypes.size(); i++) {
+ mTypes.set(i, mTypes.get(i).toLowerCase());
+ apnBitmap |= getApnBitmask(mTypes.get(i));
+ }
+ this.mTypesBitmap = apnBitmap;
+ return this;
+ }
+
+ /**
+ * Sets the unique database id for this entry.
+ *
+ * @param id the unique database id to set for this entry
+ */
+ public Builder setId(int id) {
+ this.mId = id;
+ return this;
+ }
+
+ /**
+ * Set the numeric operator ID for the APN.
+ *
+ * @param operatorNumeric the numeric operator ID to set for this entry
+ */
+ public Builder setOperatorNumeric(String operatorNumeric) {
+ this.mOperatorNumeric = operatorNumeric;
+ return this;
+ }
+
+ /**
+ * Sets the protocol to use to connect to this APN.
+ *
+ * One of the {@code PDP_type} values in TS 27.007 section 10.1.1.
+ * Example of possible values: {@link #PROTOCOL_IP}, {@link #PROTOCOL_IPV6}.
+ *
+ * @param protocol the protocol to set to use to connect to this APN
+ */
+ public Builder setProtocol(@ProtocolType String protocol) {
+ this.mProtocol = protocol;
+ return this;
+ }
+
+ /**
+ * Sets the protocol to use to connect to this APN when roaming.
+ *
+ * @param roamingProtocol the protocol to set to use to connect to this APN when roaming
+ */
+ public Builder setRoamingProtocol(String roamingProtocol) {
+ this.mRoamingProtocol = roamingProtocol;
+ return this;
+ }
+
+ /**
+ * Sets the current status of APN.
+ *
+ * @param carrierEnabled the current status to set for this APN
+ */
+ public Builder setCarrierEnabled(boolean carrierEnabled) {
+ this.mCarrierEnabled = carrierEnabled;
+ return this;
+ }
+
+ /**
+ * Sets the MVNO match type for this APN.
+ *
+ * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
+ *
+ * @param mvnoType the MVNO match type to set for this APN
+ */
+ public Builder setMvnoType(@MvnoType String mvnoType) {
+ this.mMvnoType = mvnoType;
+ return this;
+ }
+
+ public ApnSetting build() {
+ return new ApnSetting(this);
+ }
+ }
+}
+
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index da51c86..ef3a183 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.net.LinkAddress;
import android.os.Parcel;
import android.os.Parcelable;
@@ -40,7 +41,7 @@
private final int mActive;
private final String mType;
private final String mIfname;
- private final List<InterfaceAddress> mAddresses;
+ private final List<LinkAddress> mAddresses;
private final List<InetAddress> mDnses;
private final List<InetAddress> mGateways;
private final List<String> mPcscfs;
@@ -71,7 +72,7 @@
*/
public DataCallResponse(int status, int suggestedRetryTime, int cid, int active,
@Nullable String type, @Nullable String ifname,
- @Nullable List<InterfaceAddress> addresses,
+ @Nullable List<LinkAddress> addresses,
@Nullable List<InetAddress> dnses,
@Nullable List<InetAddress> gateways,
@Nullable List<String> pcscfs, int mtu) {
@@ -96,7 +97,7 @@
mType = source.readString();
mIfname = source.readString();
mAddresses = new ArrayList<>();
- source.readList(mAddresses, InterfaceAddress.class.getClassLoader());
+ source.readList(mAddresses, LinkAddress.class.getClassLoader());
mDnses = new ArrayList<>();
source.readList(mDnses, InetAddress.class.getClassLoader());
mGateways = new ArrayList<>();
@@ -140,10 +141,10 @@
public String getIfname() { return mIfname; }
/**
- * @return A list of {@link InterfaceAddress}
+ * @return A list of {@link LinkAddress}
*/
@NonNull
- public List<InterfaceAddress> getAddresses() { return mAddresses; }
+ public List<LinkAddress> getAddresses() { return mAddresses; }
/**
* @return A list of DNS server addresses, e.g., "192.0.1.3" or
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
new file mode 100644
index 0000000..ea08175
--- /dev/null
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -0,0 +1,540 @@
+/*
+ * Copyright 2017 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.telephony.data;
+
+import android.annotation.CallSuper;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.net.LinkProperties;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Base class of data service. Services that extend DataService must register the service in
+ * their AndroidManifest to be detected by the framework. They must be protected by the permission
+ * "android.permission.BIND_DATA_SERVICE". The data service definition in the manifest must follow
+ * the following format:
+ * ...
+ * <service android:name=".xxxDataService"
+ * android:permission="android.permission.BIND_DATA_SERVICE" >
+ * <intent-filter>
+ * <action android:name="android.telephony.data.DataService" />
+ * </intent-filter>
+ * </service>
+ * @hide
+ */
+@SystemApi
+public abstract class DataService extends Service {
+ private static final String TAG = DataService.class.getSimpleName();
+
+ public static final String DATA_SERVICE_INTERFACE = "android.telephony.data.DataService";
+ public static final String DATA_SERVICE_EXTRA_SLOT_ID = "android.telephony.data.extra.SLOT_ID";
+
+ private static final int DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE = 1;
+ private static final int DATA_SERVICE_REQUEST_SETUP_DATA_CALL = 2;
+ private static final int DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL = 3;
+ private static final int DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN = 4;
+ private static final int DATA_SERVICE_REQUEST_SET_DATA_PROFILE = 5;
+ private static final int DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST = 6;
+ private static final int DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED = 7;
+ private static final int DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED = 8;
+ private static final int DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED = 9;
+
+ private final HandlerThread mHandlerThread;
+
+ private final DataServiceHandler mHandler;
+
+ private final SparseArray<DataServiceProvider> mServiceMap = new SparseArray<>();
+
+ private final SparseArray<IDataServiceWrapper> mBinderMap = new SparseArray<>();
+
+ /**
+ * The abstract class of the actual data service implementation. The data service provider
+ * must extend this class to support data connection. Note that each instance of data service
+ * provider is associated with one physical SIM slot.
+ */
+ public class DataServiceProvider {
+
+ private final int mSlotId;
+
+ private final List<IDataServiceCallback> mDataCallListChangedCallbacks = new ArrayList<>();
+
+ /**
+ * Constructor
+ * @param slotId SIM slot id the data service provider associated with.
+ */
+ public DataServiceProvider(int slotId) {
+ mSlotId = slotId;
+ }
+
+ /**
+ * @return SIM slot id the data service provider associated with.
+ */
+ public final int getSlotId() {
+ return mSlotId;
+ }
+
+ /**
+ * Setup a data connection. The data service provider must implement this method to support
+ * establishing a packet data connection. When completed or error, the service must invoke
+ * the provided callback to notify the platform.
+ *
+ * @param accessNetworkType Access network type that the data call will be established on.
+ * Must be one of {@link AccessNetworkConstants.AccessNetworkType}.
+ * @param dataProfile Data profile used for data call setup. See {@link DataProfile}
+ * @param isRoaming True if the device is data roaming.
+ * @param allowRoaming True if data roaming is allowed by the user.
+ * @param isHandover True if the request is for IWLAN handover.
+ * @param linkProperties If {@code isHandover} is true, this is the link properties of the
+ * existing data connection, otherwise null.
+ * @param callback The result callback for this request.
+ */
+ public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+ boolean allowRoaming, boolean isHandover,
+ LinkProperties linkProperties, DataServiceCallback callback) {
+ // The default implementation is to return unsupported.
+ callback.onSetupDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
+ }
+
+ /**
+ * Deactivate a data connection. The data service provider must implement this method to
+ * support data connection tear down. When completed or error, the service must invoke the
+ * provided callback to notify the platform.
+ *
+ * @param cid Call id returned in the callback of {@link DataServiceProvider#setupDataCall(
+ * int, DataProfile, boolean, boolean, boolean, LinkProperties, DataServiceCallback)}.
+ * @param reasonRadioShutDown True if the deactivate request reason is device shut down.
+ * @param isHandover True if the request is for IWLAN handover.
+ * @param callback The result callback for this request.
+ */
+ public void deactivateDataCall(int cid, boolean reasonRadioShutDown, boolean isHandover,
+ DataServiceCallback callback) {
+ // The default implementation is to return unsupported.
+ callback.onDeactivateDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+ }
+
+ /**
+ * Set an APN to initial attach network.
+ *
+ * @param dataProfile Data profile used for data call setup. See {@link DataProfile}.
+ * @param isRoaming True if the device is data roaming.
+ * @param callback The result callback for this request.
+ */
+ public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
+ DataServiceCallback callback) {
+ // The default implementation is to return unsupported.
+ callback.onSetInitialAttachApnComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+ }
+
+ /**
+ * Send current carrier's data profiles to the data service for data call setup. This is
+ * only for CDMA carrier that can change the profile through OTA. The data service should
+ * always uses the latest data profile sent by the framework.
+ *
+ * @param dps A list of data profiles.
+ * @param isRoaming True if the device is data roaming.
+ * @param callback The result callback for this request.
+ */
+ public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
+ DataServiceCallback callback) {
+ // The default implementation is to return unsupported.
+ callback.onSetDataProfileComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+ }
+
+ /**
+ * Get the active data call list.
+ *
+ * @param callback The result callback for this request.
+ */
+ public void getDataCallList(DataServiceCallback callback) {
+ // The default implementation is to return unsupported.
+ callback.onGetDataCallListComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
+ }
+
+ private void registerForDataCallListChanged(IDataServiceCallback callback) {
+ synchronized (mDataCallListChangedCallbacks) {
+ mDataCallListChangedCallbacks.add(callback);
+ }
+ }
+
+ private void unregisterForDataCallListChanged(IDataServiceCallback callback) {
+ synchronized (mDataCallListChangedCallbacks) {
+ mDataCallListChangedCallbacks.remove(callback);
+ }
+ }
+
+ /**
+ * Notify the system that current data call list changed. Data service must invoke this
+ * method whenever there is any data call status changed.
+ *
+ * @param dataCallList List of the current active data call.
+ */
+ public final void notifyDataCallListChanged(List<DataCallResponse> dataCallList) {
+ synchronized (mDataCallListChangedCallbacks) {
+ for (IDataServiceCallback callback : mDataCallListChangedCallbacks) {
+ mHandler.obtainMessage(DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED, mSlotId,
+ 0, new DataCallListChangedIndication(dataCallList, callback))
+ .sendToTarget();
+ }
+ }
+ }
+
+ /**
+ * Called when the instance of data service is destroyed (e.g. got unbind or binder died).
+ */
+ @CallSuper
+ protected void onDestroy() {
+ mDataCallListChangedCallbacks.clear();
+ }
+ }
+
+ private static final class SetupDataCallRequest {
+ public final int accessNetworkType;
+ public final DataProfile dataProfile;
+ public final boolean isRoaming;
+ public final boolean allowRoaming;
+ public final boolean isHandover;
+ public final LinkProperties linkProperties;
+ public final IDataServiceCallback callback;
+ SetupDataCallRequest(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+ boolean allowRoaming, boolean isHandover,
+ LinkProperties linkProperties, IDataServiceCallback callback) {
+ this.accessNetworkType = accessNetworkType;
+ this.dataProfile = dataProfile;
+ this.isRoaming = isRoaming;
+ this.allowRoaming = allowRoaming;
+ this.linkProperties = linkProperties;
+ this.isHandover = isHandover;
+ this.callback = callback;
+ }
+ }
+
+ private static final class DeactivateDataCallRequest {
+ public final int cid;
+ public final boolean reasonRadioShutDown;
+ public final boolean isHandover;
+ public final IDataServiceCallback callback;
+ DeactivateDataCallRequest(int cid, boolean reasonRadioShutDown, boolean isHandover,
+ IDataServiceCallback callback) {
+ this.cid = cid;
+ this.reasonRadioShutDown = reasonRadioShutDown;
+ this.isHandover = isHandover;
+ this.callback = callback;
+ }
+ }
+
+ private static final class SetInitialAttachApnRequest {
+ public final DataProfile dataProfile;
+ public final boolean isRoaming;
+ public final IDataServiceCallback callback;
+ SetInitialAttachApnRequest(DataProfile dataProfile, boolean isRoaming,
+ IDataServiceCallback callback) {
+ this.dataProfile = dataProfile;
+ this.isRoaming = isRoaming;
+ this.callback = callback;
+ }
+ }
+
+ private static final class SetDataProfileRequest {
+ public final List<DataProfile> dps;
+ public final boolean isRoaming;
+ public final IDataServiceCallback callback;
+ SetDataProfileRequest(List<DataProfile> dps, boolean isRoaming,
+ IDataServiceCallback callback) {
+ this.dps = dps;
+ this.isRoaming = isRoaming;
+ this.callback = callback;
+ }
+ }
+
+ private static final class DataCallListChangedIndication {
+ public final List<DataCallResponse> dataCallList;
+ public final IDataServiceCallback callback;
+ DataCallListChangedIndication(List<DataCallResponse> dataCallList,
+ IDataServiceCallback callback) {
+ this.dataCallList = dataCallList;
+ this.callback = callback;
+ }
+ }
+
+ private class DataServiceHandler extends Handler {
+
+ DataServiceHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ IDataServiceCallback callback;
+ final int slotId = message.arg1;
+ DataServiceProvider service;
+
+ synchronized (mServiceMap) {
+ service = mServiceMap.get(slotId);
+ }
+
+ switch (message.what) {
+ case DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE:
+ service = createDataServiceProvider(message.arg1);
+ if (service != null) {
+ mServiceMap.put(slotId, service);
+ }
+ break;
+ case DATA_SERVICE_REQUEST_SETUP_DATA_CALL:
+ if (service == null) break;
+ SetupDataCallRequest setupDataCallRequest = (SetupDataCallRequest) message.obj;
+ service.setupDataCall(setupDataCallRequest.accessNetworkType,
+ setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming,
+ setupDataCallRequest.allowRoaming, setupDataCallRequest.isHandover,
+ setupDataCallRequest.linkProperties,
+ new DataServiceCallback(setupDataCallRequest.callback));
+
+ break;
+ case DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL:
+ if (service == null) break;
+ DeactivateDataCallRequest deactivateDataCallRequest =
+ (DeactivateDataCallRequest) message.obj;
+ service.deactivateDataCall(deactivateDataCallRequest.cid,
+ deactivateDataCallRequest.reasonRadioShutDown,
+ deactivateDataCallRequest.isHandover,
+ new DataServiceCallback(deactivateDataCallRequest.callback));
+ break;
+ case DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN:
+ if (service == null) break;
+ SetInitialAttachApnRequest setInitialAttachApnRequest =
+ (SetInitialAttachApnRequest) message.obj;
+ service.setInitialAttachApn(setInitialAttachApnRequest.dataProfile,
+ setInitialAttachApnRequest.isRoaming,
+ new DataServiceCallback(setInitialAttachApnRequest.callback));
+ break;
+ case DATA_SERVICE_REQUEST_SET_DATA_PROFILE:
+ if (service == null) break;
+ SetDataProfileRequest setDataProfileRequest =
+ (SetDataProfileRequest) message.obj;
+ service.setDataProfile(setDataProfileRequest.dps,
+ setDataProfileRequest.isRoaming,
+ new DataServiceCallback(setDataProfileRequest.callback));
+ break;
+ case DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST:
+ if (service == null) break;
+
+ service.getDataCallList(new DataServiceCallback(
+ (IDataServiceCallback) message.obj));
+ break;
+ case DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED:
+ if (service == null) break;
+ service.registerForDataCallListChanged((IDataServiceCallback) message.obj);
+ break;
+ case DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED:
+ if (service == null) break;
+ callback = (IDataServiceCallback) message.obj;
+ service.unregisterForDataCallListChanged(callback);
+ break;
+ case DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED:
+ if (service == null) break;
+ DataCallListChangedIndication indication =
+ (DataCallListChangedIndication) message.obj;
+ try {
+ indication.callback.onDataCallListChanged(indication.dataCallList);
+ } catch (RemoteException e) {
+ loge("Failed to call onDataCallListChanged. " + e);
+ }
+ break;
+ }
+ }
+ }
+
+ private DataService() {
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+
+ mHandler = new DataServiceHandler(mHandlerThread.getLooper());
+ log("Data service created");
+ }
+
+ /**
+ * Create the instance of {@link DataServiceProvider}. Data service provider must override
+ * this method to facilitate the creation of {@link DataServiceProvider} instances. The system
+ * will call this method after binding the data service for each active SIM slot id.
+ *
+ * @param slotId SIM slot id the data service associated with.
+ * @return Data service object
+ */
+ public abstract DataServiceProvider createDataServiceProvider(int slotId);
+
+ /** @hide */
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (intent == null || !DATA_SERVICE_INTERFACE.equals(intent.getAction())) {
+ loge("Unexpected intent " + intent);
+ return null;
+ }
+
+ int slotId = intent.getIntExtra(
+ DATA_SERVICE_EXTRA_SLOT_ID, SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+
+ if (!SubscriptionManager.isValidSlotIndex(slotId)) {
+ loge("Invalid slot id " + slotId);
+ return null;
+ }
+
+ log("onBind: slot id=" + slotId);
+
+ IDataServiceWrapper binder = mBinderMap.get(slotId);
+ if (binder == null) {
+ Message msg = mHandler.obtainMessage(DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE);
+ msg.arg1 = slotId;
+ msg.sendToTarget();
+
+ binder = new IDataServiceWrapper(slotId);
+ mBinderMap.put(slotId, binder);
+ }
+
+ return binder;
+ }
+
+ /** @hide */
+ @Override
+ public boolean onUnbind(Intent intent) {
+ int slotId = intent.getIntExtra(DATA_SERVICE_EXTRA_SLOT_ID,
+ SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+ if (mBinderMap.get(slotId) != null) {
+ DataServiceProvider serviceImpl;
+ synchronized (mServiceMap) {
+ serviceImpl = mServiceMap.get(slotId);
+ }
+ if (serviceImpl != null) {
+ serviceImpl.onDestroy();
+ }
+ mBinderMap.remove(slotId);
+ }
+
+ // If all clients unbinds, quit the handler thread
+ if (mBinderMap.size() == 0) {
+ mHandlerThread.quit();
+ }
+
+ return false;
+ }
+
+ /** @hide */
+ @Override
+ public void onDestroy() {
+ synchronized (mServiceMap) {
+ for (int i = 0; i < mServiceMap.size(); i++) {
+ DataServiceProvider serviceImpl = mServiceMap.get(i);
+ if (serviceImpl != null) {
+ serviceImpl.onDestroy();
+ }
+ }
+ mServiceMap.clear();
+ }
+
+ mHandlerThread.quit();
+ }
+
+ /**
+ * A wrapper around IDataService that forwards calls to implementations of {@link DataService}.
+ */
+ private class IDataServiceWrapper extends IDataService.Stub {
+
+ private final int mSlotId;
+
+ IDataServiceWrapper(int slotId) {
+ mSlotId = slotId;
+ }
+
+ @Override
+ public void setupDataCall(int accessNetworkType, DataProfile dataProfile,
+ boolean isRoaming, boolean allowRoaming, boolean isHandover,
+ LinkProperties linkProperties, IDataServiceCallback callback) {
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, mSlotId, 0,
+ new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming,
+ allowRoaming, isHandover, linkProperties, callback))
+ .sendToTarget();
+ }
+
+ @Override
+ public void deactivateDataCall(int cid, boolean reasonRadioShutDown, boolean isHandover,
+ IDataServiceCallback callback) {
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL, mSlotId, 0,
+ new DeactivateDataCallRequest(cid, reasonRadioShutDown, isHandover, callback))
+ .sendToTarget();
+ }
+
+ @Override
+ public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
+ IDataServiceCallback callback) {
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN, mSlotId, 0,
+ new SetInitialAttachApnRequest(dataProfile, isRoaming, callback))
+ .sendToTarget();
+ }
+
+ @Override
+ public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
+ IDataServiceCallback callback) {
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_DATA_PROFILE, mSlotId, 0,
+ new SetDataProfileRequest(dps, isRoaming, callback)).sendToTarget();
+ }
+
+ @Override
+ public void getDataCallList(IDataServiceCallback callback) {
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST, mSlotId, 0,
+ callback).sendToTarget();
+ }
+
+ @Override
+ public void registerForDataCallListChanged(IDataServiceCallback callback) {
+ if (callback == null) {
+ loge("Callback is null");
+ return;
+ }
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED, mSlotId,
+ 0, callback).sendToTarget();
+ }
+
+ @Override
+ public void unregisterForDataCallListChanged(IDataServiceCallback callback) {
+ if (callback == null) {
+ loge("Callback is null");
+ return;
+ }
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED, mSlotId,
+ 0, callback).sendToTarget();
+ }
+ }
+
+ private void log(String s) {
+ Rlog.d(TAG, s);
+ }
+
+ private void loge(String s) {
+ Rlog.e(TAG, s);
+ }
+}
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
new file mode 100644
index 0000000..b6a81f9
--- /dev/null
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2017 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.telephony.data;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.telephony.Rlog;
+import android.telephony.data.DataService.DataServiceProvider;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.List;
+
+/**
+ * Data service callback, which is for bound data service to invoke for solicited and unsolicited
+ * response. The caller is responsible to create a callback object for each single asynchronous
+ * request.
+ *
+ * @hide
+ */
+@SystemApi
+public class DataServiceCallback {
+
+ private static final String mTag = DataServiceCallback.class.getSimpleName();
+
+ /**
+ * Result of data requests
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({RESULT_SUCCESS, RESULT_ERROR_UNSUPPORTED, RESULT_ERROR_INVALID_ARG, RESULT_ERROR_BUSY,
+ RESULT_ERROR_ILLEGAL_STATE})
+ public @interface Result {}
+
+ /** Request is completed successfully */
+ public static final int RESULT_SUCCESS = 0;
+ /** Request is not support */
+ public static final int RESULT_ERROR_UNSUPPORTED = 1;
+ /** Request contains invalid arguments */
+ public static final int RESULT_ERROR_INVALID_ARG = 2;
+ /** Service is busy */
+ public static final int RESULT_ERROR_BUSY = 3;
+ /** Request sent in illegal state */
+ public static final int RESULT_ERROR_ILLEGAL_STATE = 4;
+
+ private final WeakReference<IDataServiceCallback> mCallback;
+
+ /** @hide */
+ public DataServiceCallback(IDataServiceCallback callback) {
+ mCallback = new WeakReference<>(callback);
+ }
+
+ /**
+ * Called to indicate result for the request {@link DataServiceProvider#setupDataCall(int,
+ * DataProfile, boolean, boolean, boolean, DataServiceCallback)}.
+ *
+ * @param result The result code. Must be one of the {@link Result}.
+ * @param response Setup data call response.
+ */
+ public void onSetupDataCallComplete(@Result int result, DataCallResponse response) {
+ IDataServiceCallback callback = mCallback.get();
+ if (callback != null) {
+ try {
+ callback.onSetupDataCallComplete(result, response);
+ } catch (RemoteException e) {
+ Rlog.e(mTag, "Failed to onSetupDataCallComplete on the remote");
+ }
+ }
+ }
+
+ /**
+ * Called to indicate result for the request {@link DataServiceProvider#deactivateDataCall(int,
+ * boolean, boolean, DataServiceCallback)}.
+ *
+ * @param result The result code. Must be one of the {@link Result}.
+ */
+ public void onDeactivateDataCallComplete(@Result int result) {
+ IDataServiceCallback callback = mCallback.get();
+ if (callback != null) {
+ try {
+ callback.onDeactivateDataCallComplete(result);
+ } catch (RemoteException e) {
+ Rlog.e(mTag, "Failed to onDeactivateDataCallComplete on the remote");
+ }
+ }
+ }
+
+ /**
+ * Called to indicate result for the request {@link DataServiceProvider#setInitialAttachApn(
+ * DataProfile, boolean, DataServiceCallback)}.
+ *
+ * @param result The result code. Must be one of the {@link Result}.
+ */
+ public void onSetInitialAttachApnComplete(@Result int result) {
+ IDataServiceCallback callback = mCallback.get();
+ if (callback != null) {
+ try {
+ callback.onSetInitialAttachApnComplete(result);
+ } catch (RemoteException e) {
+ Rlog.e(mTag, "Failed to onSetInitialAttachApnComplete on the remote");
+ }
+ }
+ }
+
+ /**
+ * Called to indicate result for the request {@link DataServiceProvider#setDataProfile(List,
+ * boolean, DataServiceCallback)}.
+ *
+ * @param result The result code. Must be one of the {@link Result}.
+ */
+ @SystemApi
+ public void onSetDataProfileComplete(@Result int result) {
+ IDataServiceCallback callback = mCallback.get();
+ if (callback != null) {
+ try {
+ callback.onSetDataProfileComplete(result);
+ } catch (RemoteException e) {
+ Rlog.e(mTag, "Failed to onSetDataProfileComplete on the remote");
+ }
+ }
+ }
+
+ /**
+ * Called to indicate result for the request {@link DataServiceProvider#getDataCallList(
+ * DataServiceCallback)}.
+ *
+ * @param result The result code. Must be one of the {@link Result}.
+ * @param dataCallList List of the current active data connection.
+ */
+ public void onGetDataCallListComplete(@Result int result, List<DataCallResponse> dataCallList) {
+ IDataServiceCallback callback = mCallback.get();
+ if (callback != null) {
+ try {
+ callback.onGetDataCallListComplete(result, dataCallList);
+ } catch (RemoteException e) {
+ Rlog.e(mTag, "Failed to onGetDataCallListComplete on the remote");
+ }
+ }
+ }
+
+ /**
+ * Called to indicate that data connection list changed.
+ *
+ * @param dataCallList List of the current active data connection.
+ */
+ public void onDataCallListChanged(List<DataCallResponse> dataCallList) {
+ IDataServiceCallback callback = mCallback.get();
+ if (callback != null) {
+ try {
+ callback.onDataCallListChanged(dataCallList);
+ } catch (RemoteException e) {
+ Rlog.e(mTag, "Failed to onDataCallListChanged on the remote");
+ }
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl
new file mode 100644
index 0000000..4eaaa252
--- /dev/null
+++ b/telephony/java/android/telephony/data/IDataService.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 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.telephony.data;
+
+import android.net.LinkProperties;
+import android.telephony.data.DataProfile;
+import android.telephony.data.IDataServiceCallback;
+
+/**
+ * {@hide}
+ */
+oneway interface IDataService
+{
+ void setupDataCall(int accessNetwork, in DataProfile dataProfile, boolean isRoaming,
+ boolean allowRoaming, boolean isHandover, in LinkProperties linkProperties,
+ IDataServiceCallback callback);
+ void deactivateDataCall(int cid, boolean reasonRadioShutDown, boolean isHandover,
+ IDataServiceCallback callback);
+ void setInitialAttachApn(in DataProfile dataProfile, boolean isRoaming,
+ IDataServiceCallback callback);
+ void setDataProfile(in List<DataProfile> dps, boolean isRoaming, IDataServiceCallback callback);
+ void getDataCallList(IDataServiceCallback callback);
+ void registerForDataCallListChanged(IDataServiceCallback callback);
+ void unregisterForDataCallListChanged(IDataServiceCallback callback);
+}
diff --git a/telephony/java/android/telephony/data/IDataServiceCallback.aidl b/telephony/java/android/telephony/data/IDataServiceCallback.aidl
new file mode 100644
index 0000000..856185b
--- /dev/null
+++ b/telephony/java/android/telephony/data/IDataServiceCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017 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.telephony.data;
+
+import android.telephony.data.DataCallResponse;
+
+/**
+ * The call back interface
+ * @hide
+ */
+oneway interface IDataServiceCallback
+{
+ void onSetupDataCallComplete(int result, in DataCallResponse dataCallResponse);
+ void onDeactivateDataCallComplete(int result);
+ void onSetInitialAttachApnComplete(int result);
+ void onSetDataProfileComplete(int result);
+ void onGetDataCallListComplete(int result, in List<DataCallResponse> dataCallList);
+ void onDataCallListChanged(in List<DataCallResponse> dataCallList);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.java b/telephony/java/android/telephony/data/InterfaceAddress.java
deleted file mode 100644
index 00d212a..0000000
--- a/telephony/java/android/telephony/data/InterfaceAddress.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright 2017 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.telephony.data;
-
-import android.annotation.SystemApi;
-import android.net.NetworkUtils;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-/**
- * This class represents a Network Interface address. In short it's an IP address, a subnet mask
- * when the address is an IPv4 one. An IP address and a network prefix length in the case of IPv6
- * address.
- *
- * @hide
- */
-@SystemApi
-public final class InterfaceAddress implements Parcelable {
-
- private final InetAddress mInetAddress;
-
- private final int mPrefixLength;
-
- /**
- * @param inetAddress A {@link InetAddress} of the address
- * @param prefixLength The network prefix length for this address.
- */
- public InterfaceAddress(InetAddress inetAddress, int prefixLength) {
- mInetAddress = inetAddress;
- mPrefixLength = prefixLength;
- }
-
- /**
- * @param address The address in string format
- * @param prefixLength The network prefix length for this address.
- * @throws UnknownHostException
- */
- public InterfaceAddress(String address, int prefixLength) throws UnknownHostException {
- InetAddress ia;
- try {
- ia = NetworkUtils.numericToInetAddress(address);
- } catch (IllegalArgumentException e) {
- throw new UnknownHostException("Non-numeric ip addr=" + address);
- }
- mInetAddress = ia;
- mPrefixLength = prefixLength;
- }
-
- public InterfaceAddress(Parcel source) {
- mInetAddress = (InetAddress) source.readSerializable();
- mPrefixLength = source.readInt();
- }
-
- /**
- * @return an InetAddress for this address.
- */
- public InetAddress getAddress() { return mInetAddress; }
-
- /**
- * @return The network prefix length for this address.
- */
- public int getNetworkPrefixLength() { return mPrefixLength; }
-
- @Override
- public boolean equals (Object o) {
- if (this == o) return true;
-
- if (o == null || !(o instanceof InterfaceAddress)) {
- return false;
- }
-
- InterfaceAddress other = (InterfaceAddress) o;
- return this.mInetAddress.equals(other.mInetAddress)
- && this.mPrefixLength == other.mPrefixLength;
- }
-
- @Override
- public int hashCode() {
- return mInetAddress.hashCode() * 31 + mPrefixLength * 37;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public String toString() {
- return mInetAddress + "/" + mPrefixLength;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeSerializable(mInetAddress);
- dest.writeInt(mPrefixLength);
- }
-
- public static final Parcelable.Creator<InterfaceAddress> CREATOR =
- new Parcelable.Creator<InterfaceAddress>() {
- @Override
- public InterfaceAddress createFromParcel(Parcel source) {
- return new InterfaceAddress(source);
- }
-
- @Override
- public InterfaceAddress[] newArray(int size) {
- return new InterfaceAddress[size];
- }
- };
-}
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
new file mode 100644
index 0000000..b9ed005
--- /dev/null
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -0,0 +1,663 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.euicc;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.euicc.EuiccProfileInfo;
+import android.util.Log;
+
+import com.android.internal.telephony.euicc.IAuthenticateServerCallback;
+import com.android.internal.telephony.euicc.ICancelSessionCallback;
+import com.android.internal.telephony.euicc.IDeleteProfileCallback;
+import com.android.internal.telephony.euicc.IDisableProfileCallback;
+import com.android.internal.telephony.euicc.IEuiccCardController;
+import com.android.internal.telephony.euicc.IGetAllProfilesCallback;
+import com.android.internal.telephony.euicc.IGetDefaultSmdpAddressCallback;
+import com.android.internal.telephony.euicc.IGetEuiccChallengeCallback;
+import com.android.internal.telephony.euicc.IGetEuiccInfo1Callback;
+import com.android.internal.telephony.euicc.IGetEuiccInfo2Callback;
+import com.android.internal.telephony.euicc.IGetProfileCallback;
+import com.android.internal.telephony.euicc.IGetRulesAuthTableCallback;
+import com.android.internal.telephony.euicc.IGetSmdsAddressCallback;
+import com.android.internal.telephony.euicc.IListNotificationsCallback;
+import com.android.internal.telephony.euicc.ILoadBoundProfilePackageCallback;
+import com.android.internal.telephony.euicc.IPrepareDownloadCallback;
+import com.android.internal.telephony.euicc.IRemoveNotificationFromListCallback;
+import com.android.internal.telephony.euicc.IResetMemoryCallback;
+import com.android.internal.telephony.euicc.IRetrieveNotificationCallback;
+import com.android.internal.telephony.euicc.IRetrieveNotificationListCallback;
+import com.android.internal.telephony.euicc.ISetDefaultSmdpAddressCallback;
+import com.android.internal.telephony.euicc.ISetNicknameCallback;
+import com.android.internal.telephony.euicc.ISwitchToProfileCallback;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * EuiccCardManager is the application interface to an eSIM card.
+ *
+ * @hide
+ *
+ * TODO(b/35851809): Make this a SystemApi.
+ */
+public class EuiccCardManager {
+ private static final String TAG = "EuiccCardManager";
+
+ /** Reason for canceling a profile download session */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "CANCEL_REASON_" }, value = {
+ CANCEL_REASON_END_USER_REJECTED,
+ CANCEL_REASON_POSTPONED,
+ CANCEL_REASON_TIMEOUT,
+ CANCEL_REASON_PPR_NOT_ALLOWED
+ })
+ public @interface CancelReason {}
+
+ /**
+ * The end user has rejected the download. The profile will be put into the error state and
+ * cannot be downloaded again without the operator's change.
+ */
+ public static final int CANCEL_REASON_END_USER_REJECTED = 0;
+
+ /** The download has been postponed and can be restarted later. */
+ public static final int CANCEL_REASON_POSTPONED = 1;
+
+ /** The download has been timed out and can be restarted later. */
+ public static final int CANCEL_REASON_TIMEOUT = 2;
+
+ /**
+ * The profile to be downloaded cannot be installed due to its policy rule is not allowed by
+ * the RAT (Rules Authorisation Table) on the eUICC or by other installed profiles. The
+ * download can be restarted later.
+ */
+ public static final int CANCEL_REASON_PPR_NOT_ALLOWED = 3;
+
+ /** Options for resetting eUICC memory */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "RESET_OPTION_" }, value = {
+ RESET_OPTION_DELETE_OPERATIONAL_PROFILES,
+ RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES,
+ RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS
+ })
+ public @interface ResetOption {}
+
+ /** Deletes all operational profiles. */
+ public static final int RESET_OPTION_DELETE_OPERATIONAL_PROFILES = 1;
+
+ /** Deletes all field-loaded testing profiles. */
+ public static final int RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES = 1 << 1;
+
+ /** Resets the default SM-DP+ address. */
+ public static final int RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS = 1 << 2;
+
+ /** Result code of execution with no error. */
+ public static final int RESULT_OK = 0;
+
+ /**
+ * Callback to receive the result of an eUICC card API.
+ *
+ * @param <T> Type of the result.
+ */
+ public interface ResultCallback<T> {
+ /**
+ * This method will be called when an eUICC card API call is completed.
+ *
+ * @param resultCode This can be {@link #RESULT_OK} or other positive values returned by the
+ * eUICC.
+ * @param result The result object. It can be null if the {@code resultCode} is not
+ * {@link #RESULT_OK}.
+ */
+ void onComplete(int resultCode, T result);
+ }
+
+ private final Context mContext;
+
+ /** @hide */
+ public EuiccCardManager(Context context) {
+ mContext = context;
+ }
+
+ private IEuiccCardController getIEuiccCardController() {
+ return IEuiccCardController.Stub.asInterface(
+ ServiceManager.getService("euicc_card_controller"));
+ }
+
+ /**
+ * Gets all the profiles on eUicc.
+ *
+ * @param callback The callback to get the result code and all the profiles.
+ */
+ public void getAllProfiles(ResultCallback<EuiccProfileInfo[]> callback) {
+ try {
+ getIEuiccCardController().getAllProfiles(mContext.getOpPackageName(),
+ new IGetAllProfilesCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, EuiccProfileInfo[] profiles) {
+ callback.onComplete(resultCode, profiles);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getAllProfiles", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the profile of the given iccid.
+ *
+ * @param iccid The iccid of the profile.
+ * @param callback The callback to get the result code and profile.
+ */
+ public void getProfile(String iccid, ResultCallback<EuiccProfileInfo> callback) {
+ try {
+ getIEuiccCardController().getProfile(mContext.getOpPackageName(), iccid,
+ new IGetProfileCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, EuiccProfileInfo profile) {
+ callback.onComplete(resultCode, profile);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getProfile", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Disables the profile of the given iccid.
+ *
+ * @param iccid The iccid of the profile.
+ * @param refresh Whether sending the REFRESH command to modem.
+ * @param callback The callback to get the result code.
+ */
+ public void disableProfile(String iccid, boolean refresh, ResultCallback<Void> callback) {
+ try {
+ getIEuiccCardController().disableProfile(mContext.getOpPackageName(), iccid, refresh,
+ new IDisableProfileCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode) {
+ callback.onComplete(resultCode, null);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling disableProfile", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Switches from the current profile to another profile. The current profile will be disabled
+ * and the specified profile will be enabled.
+ *
+ * @param iccid The iccid of the profile to switch to.
+ * @param refresh Whether sending the REFRESH command to modem.
+ * @param callback The callback to get the result code and the EuiccProfileInfo enabled.
+ */
+ public void switchToProfile(String iccid, boolean refresh,
+ ResultCallback<EuiccProfileInfo> callback) {
+ try {
+ getIEuiccCardController().switchToProfile(mContext.getOpPackageName(), iccid, refresh,
+ new ISwitchToProfileCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, EuiccProfileInfo profile) {
+ callback.onComplete(resultCode, profile);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling switchToProfile", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the EID of the eUICC.
+ *
+ * @return The EID.
+ */
+ public String getEid() {
+ try {
+ return getIEuiccCardController().getEid();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getEid", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets the nickname of the profile of the given iccid.
+ *
+ * @param iccid The iccid of the profile.
+ * @param nickname The nickname of the profile.
+ * @param callback The callback to get the result code.
+ */
+ public void setNickname(String iccid, String nickname, ResultCallback<Void> callback) {
+ try {
+ getIEuiccCardController().setNickname(mContext.getOpPackageName(), iccid, nickname,
+ new ISetNicknameCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode) {
+ callback.onComplete(resultCode, null);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling setNickname", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Deletes the profile of the given iccid from eUICC.
+ *
+ * @param iccid The iccid of the profile.
+ * @param callback The callback to get the result code.
+ */
+ public void deleteProfile(String iccid, ResultCallback<Void> callback) {
+ try {
+ getIEuiccCardController().deleteProfile(mContext.getOpPackageName(), iccid,
+ new IDeleteProfileCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode) {
+ callback.onComplete(resultCode, null);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling deleteProfile", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Resets the eUICC memory.
+ *
+ * @param options Bits of the options of resetting which parts of the eUICC memory. See
+ * EuiccCard for details.
+ * @param callback The callback to get the result code.
+ */
+ public void resetMemory(@ResetOption int options, ResultCallback<Void> callback) {
+ try {
+ getIEuiccCardController().resetMemory(mContext.getOpPackageName(), options,
+ new IResetMemoryCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode) {
+ callback.onComplete(resultCode, null);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling resetMemory", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the default SM-DP+ address from eUICC.
+ *
+ * @param callback The callback to get the result code and the default SM-DP+ address.
+ */
+ public void getDefaultSmdpAddress(ResultCallback<String> callback) {
+ try {
+ getIEuiccCardController().getDefaultSmdpAddress(mContext.getOpPackageName(),
+ new IGetDefaultSmdpAddressCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, String address) {
+ callback.onComplete(resultCode, address);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getDefaultSmdpAddress", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the SM-DS address from eUICC.
+ *
+ * @param callback The callback to get the result code and the SM-DS address.
+ */
+ public void getSmdsAddress(ResultCallback<String> callback) {
+ try {
+ getIEuiccCardController().getSmdsAddress(mContext.getOpPackageName(),
+ new IGetSmdsAddressCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, String address) {
+ callback.onComplete(resultCode, address);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getSmdsAddress", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets the default SM-DP+ address of eUICC.
+ *
+ * @param defaultSmdpAddress The default SM-DP+ address to set.
+ * @param callback The callback to get the result code.
+ */
+ public void setDefaultSmdpAddress(String defaultSmdpAddress, ResultCallback<Void> callback) {
+ try {
+ getIEuiccCardController().setDefaultSmdpAddress(mContext.getOpPackageName(),
+ defaultSmdpAddress,
+ new ISetDefaultSmdpAddressCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode) {
+ callback.onComplete(resultCode, null);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling setDefaultSmdpAddress", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets Rules Authorisation Table.
+ *
+ * @param callback the callback to get the result code and the rule authorisation table.
+ */
+ public void getRulesAuthTable(ResultCallback<EuiccRulesAuthTable> callback) {
+ try {
+ getIEuiccCardController().getRulesAuthTable(mContext.getOpPackageName(),
+ new IGetRulesAuthTableCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, EuiccRulesAuthTable rat) {
+ callback.onComplete(resultCode, rat);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getRulesAuthTable", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the eUICC challenge for new profile downloading.
+ *
+ * @param callback the callback to get the result code and the challenge.
+ */
+ public void getEuiccChallenge(ResultCallback<byte[]> callback) {
+ try {
+ getIEuiccCardController().getEuiccChallenge(mContext.getOpPackageName(),
+ new IGetEuiccChallengeCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, byte[] challenge) {
+ callback.onComplete(resultCode, challenge);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getEuiccChallenge", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the eUICC info1 defined in GSMA RSP v2.0+ for new profile downloading.
+ *
+ * @param callback the callback to get the result code and the info1.
+ */
+ public void getEuiccInfo1(ResultCallback<byte[]> callback) {
+ try {
+ getIEuiccCardController().getEuiccInfo1(mContext.getOpPackageName(),
+ new IGetEuiccInfo1Callback.Stub() {
+ @Override
+ public void onComplete(int resultCode, byte[] info) {
+ callback.onComplete(resultCode, info);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getEuiccInfo1", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the eUICC info2 defined in GSMA RSP v2.0+ for new profile downloading.
+ *
+ * @param callback the callback to get the result code and the info2.
+ */
+ public void getEuiccInfo2(ResultCallback<byte[]> callback) {
+ try {
+ getIEuiccCardController().getEuiccInfo2(mContext.getOpPackageName(),
+ new IGetEuiccInfo2Callback.Stub() {
+ @Override
+ public void onComplete(int resultCode, byte[] info) {
+ callback.onComplete(resultCode, info);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getEuiccInfo2", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Authenticates the SM-DP+ server by the eUICC.
+ *
+ * @param matchingId the activation code token defined in GSMA RSP v2.0+ or empty when it is not
+ * required.
+ * @param serverSigned1 ASN.1 data in byte array signed and returned by the SM-DP+ server.
+ * @param serverSignature1 ASN.1 data in byte array indicating a SM-DP+ signature which is
+ * returned by SM-DP+ server.
+ * @param euiccCiPkIdToBeUsed ASN.1 data in byte array indicating CI Public Key Identifier to be
+ * used by the eUICC for signature which is returned by SM-DP+ server. This is defined in
+ * GSMA RSP v2.0+.
+ * @param serverCertificate ASN.1 data in byte array indicating SM-DP+ Certificate returned by
+ * SM-DP+ server.
+ * @param callback the callback to get the result code and a byte array which represents a
+ * {@code AuthenticateServerResponse} defined in GSMA RSP v2.0+.
+ */
+ public void authenticateServer(String matchingId, byte[] serverSigned1,
+ byte[] serverSignature1, byte[] euiccCiPkIdToBeUsed, byte[] serverCertificate,
+ ResultCallback<byte[]> callback) {
+ try {
+ getIEuiccCardController().authenticateServer(
+ mContext.getOpPackageName(),
+ matchingId,
+ serverSigned1,
+ serverSignature1,
+ euiccCiPkIdToBeUsed,
+ serverCertificate,
+ new IAuthenticateServerCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, byte[] response) {
+ callback.onComplete(resultCode, response);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling authenticateServer", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Prepares the profile download request sent to SM-DP+.
+ *
+ * @param hashCc the hash of confirmation code. It can be null if there is no confirmation code
+ * required.
+ * @param smdpSigned2 ASN.1 data in byte array indicating the data to be signed by the SM-DP+
+ * returned by SM-DP+ server.
+ * @param smdpSignature2 ASN.1 data in byte array indicating the SM-DP+ signature returned by
+ * SM-DP+ server.
+ * @param smdpCertificate ASN.1 data in byte array indicating the SM-DP+ Certificate returned
+ * by SM-DP+ server.
+ * @param callback the callback to get the result code and a byte array which represents a
+ * {@code PrepareDownloadResponse} defined in GSMA RSP v2.0+
+ */
+ public void prepareDownload(@Nullable byte[] hashCc, byte[] smdpSigned2,
+ byte[] smdpSignature2, byte[] smdpCertificate, ResultCallback<byte[]> callback) {
+ try {
+ getIEuiccCardController().prepareDownload(
+ mContext.getOpPackageName(),
+ hashCc,
+ smdpSigned2,
+ smdpSignature2,
+ smdpCertificate,
+ new IPrepareDownloadCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, byte[] response) {
+ callback.onComplete(resultCode, response);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling prepareDownload", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Loads a downloaded bound profile package onto the eUICC.
+ *
+ * @param boundProfilePackage the Bound Profile Package data returned by SM-DP+ server.
+ * @param callback the callback to get the result code and a byte array which represents a
+ * {@code LoadBoundProfilePackageResponse} defined in GSMA RSP v2.0+.
+ */
+ public void loadBoundProfilePackage(byte[] boundProfilePackage,
+ ResultCallback<byte[]> callback) {
+ try {
+ getIEuiccCardController().loadBoundProfilePackage(
+ mContext.getOpPackageName(),
+ boundProfilePackage,
+ new ILoadBoundProfilePackageCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, byte[] response) {
+ callback.onComplete(resultCode, response);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling loadBoundProfilePackage", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Cancels the current profile download session.
+ *
+ * @param transactionId the transaction ID returned by SM-DP+ server.
+ * @param reason the cancel reason.
+ * @param callback the callback to get the result code and an byte[] which represents a
+ * {@code CancelSessionResponse} defined in GSMA RSP v2.0+.
+ */
+ public void cancelSession(byte[] transactionId, @CancelReason int reason,
+ ResultCallback<byte[]> callback) {
+ try {
+ getIEuiccCardController().cancelSession(
+ mContext.getOpPackageName(),
+ transactionId,
+ reason,
+ new ICancelSessionCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, byte[] response) {
+ callback.onComplete(resultCode, response);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling cancelSession", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Lists all notifications of the given {@code notificationEvents}.
+ *
+ * @param events bits of the event types ({@link EuiccNotification.Event}) to list.
+ * @param callback the callback to get the result code and the list of notifications.
+ */
+ public void listNotifications(@EuiccNotification.Event int events,
+ ResultCallback<EuiccNotification[]> callback) {
+ try {
+ getIEuiccCardController().listNotifications(mContext.getOpPackageName(), events,
+ new IListNotificationsCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, EuiccNotification[] notifications) {
+ callback.onComplete(resultCode, notifications);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling listNotifications", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Retrieves contents of all notification of the given {@code events}.
+ *
+ * @param events bits of the event types ({@link EuiccNotification.Event}) to list.
+ * @param callback the callback to get the result code and the list of notifications.
+ */
+ public void retrieveNotificationList(@EuiccNotification.Event int events,
+ ResultCallback<EuiccNotification[]> callback) {
+ try {
+ getIEuiccCardController().retrieveNotificationList(mContext.getOpPackageName(), events,
+ new IRetrieveNotificationListCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, EuiccNotification[] notifications) {
+ callback.onComplete(resultCode, notifications);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling retrieveNotificationList", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Retrieves the content of a notification of the given {@code seqNumber}.
+ *
+ * @param seqNumber the sequence number of the notification.
+ * @param callback the callback to get the result code and the notification.
+ */
+ public void retrieveNotification(int seqNumber, ResultCallback<EuiccNotification> callback) {
+ try {
+ getIEuiccCardController().retrieveNotification(mContext.getOpPackageName(), seqNumber,
+ new IRetrieveNotificationCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, EuiccNotification notification) {
+ callback.onComplete(resultCode, notification);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling retrieveNotification", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes a notification from eUICC.
+ *
+ * @param seqNumber the sequence number of the notification.
+ * @param callback the callback to get the result code.
+ */
+ public void removeNotificationFromList(int seqNumber, ResultCallback<Void> callback) {
+ try {
+ getIEuiccCardController().removeNotificationFromList(
+ mContext.getOpPackageName(),
+ seqNumber,
+ new IRemoveNotificationFromListCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode) {
+ callback.onComplete(resultCode, null);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling removeNotificationFromList", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/euicc/EuiccNotification.aidl
similarity index 81%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/android/telephony/euicc/EuiccNotification.aidl
index d750363..dad770d 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/android/telephony/euicc/EuiccNotification.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-/** @hide */
-package android.telephony.data;
+package android.telephony.euicc;
-parcelable InterfaceAddress;
+parcelable EuiccNotification;
diff --git a/telephony/java/android/telephony/euicc/EuiccNotification.java b/telephony/java/android/telephony/euicc/EuiccNotification.java
new file mode 100644
index 0000000..ef3c1ce
--- /dev/null
+++ b/telephony/java/android/telephony/euicc/EuiccNotification.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.euicc;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * This represents a signed notification which is defined in SGP.22. It can be either a profile
+ * installation result or a notification generated for profile operations (e.g., enabling,
+ * disabling, or deleting).
+ *
+ * @hide
+ *
+ * TODO(b/35851809): Make this a @SystemApi.
+ */
+public class EuiccNotification implements Parcelable {
+ /** Event */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "EVENT_" }, value = {
+ EVENT_INSTALL,
+ EVENT_ENABLE,
+ EVENT_DISABLE,
+ EVENT_DELETE
+ })
+ public @interface Event {}
+
+ /** A profile is downloaded and installed. */
+ public static final int EVENT_INSTALL = 1;
+
+ /** A profile is enabled. */
+ public static final int EVENT_ENABLE = 1 << 1;
+
+ /** A profile is disabled. */
+ public static final int EVENT_DISABLE = 1 << 2;
+
+ /** A profile is deleted. */
+ public static final int EVENT_DELETE = 1 << 3;
+
+ /** Value of the bits of all above events */
+ @Event
+ public static final int ALL_EVENTS =
+ EVENT_INSTALL | EVENT_ENABLE | EVENT_DISABLE | EVENT_DELETE;
+
+ private final int mSeq;
+ private final String mTargetAddr;
+ @Event private final int mEvent;
+ @Nullable private final byte[] mData;
+
+ /**
+ * Creates an instance.
+ *
+ * @param seq The sequence number of this notification.
+ * @param targetAddr The target server where to send this notification.
+ * @param event The event which causes this notification.
+ * @param data The data which needs to be sent to the target server. This can be null for
+ * building a list of notification metadata without data.
+ */
+ public EuiccNotification(int seq, String targetAddr, @Event int event, @Nullable byte[] data) {
+ mSeq = seq;
+ mTargetAddr = targetAddr;
+ mEvent = event;
+ mData = data;
+ }
+
+ /** @return The sequence number of this notification. */
+ public int getSeq() {
+ return mSeq;
+ }
+
+ /** @return The target server address where this notification should be sent to. */
+ public String getTargetAddr() {
+ return mTargetAddr;
+ }
+
+ /** @return The event of this notification. */
+ @Event
+ public int getEvent() {
+ return mEvent;
+ }
+
+ /** @return The notification data which needs to be sent to the target server. */
+ @Nullable
+ public byte[] getData() {
+ return mData;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+
+ EuiccNotification that = (EuiccNotification) obj;
+ return mSeq == that.mSeq
+ && Objects.equals(mTargetAddr, that.mTargetAddr)
+ && mEvent == that.mEvent
+ && Arrays.equals(mData, that.mData);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 1;
+ result = 31 * result + mSeq;
+ result = 31 * result + Objects.hashCode(mTargetAddr);
+ result = 31 * result + mEvent;
+ result = 31 * result + Arrays.hashCode(mData);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "EuiccNotification (seq="
+ + mSeq
+ + ", targetAddr="
+ + mTargetAddr
+ + ", event="
+ + mEvent
+ + ", data="
+ + (mData == null ? "null" : "byte[" + mData.length + "]")
+ + ")";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mSeq);
+ dest.writeString(mTargetAddr);
+ dest.writeInt(mEvent);
+ dest.writeByteArray(mData);
+ }
+
+ private EuiccNotification(Parcel source) {
+ mSeq = source.readInt();
+ mTargetAddr = source.readString();
+ mEvent = source.readInt();
+ mData = source.createByteArray();
+ }
+
+ public static final Creator<EuiccNotification> CREATOR =
+ new Creator<EuiccNotification>() {
+ @Override
+ public EuiccNotification createFromParcel(Parcel source) {
+ return new EuiccNotification(source);
+ }
+
+ @Override
+ public EuiccNotification[] newArray(int size) {
+ return new EuiccNotification[size];
+ }
+ };
+}
diff --git a/core/java/android/security/recoverablekeystore/KeyDerivationParameters.aidl b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.aidl
similarity index 86%
rename from core/java/android/security/recoverablekeystore/KeyDerivationParameters.aidl
rename to telephony/java/android/telephony/euicc/EuiccRulesAuthTable.aidl
index fe13179..9785a45 100644
--- a/core/java/android/security/recoverablekeystore/KeyDerivationParameters.aidl
+++ b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-package android.security.recoverablekeystore;
+package android.telephony.euicc;
-/* @hide */
-parcelable KeyDerivationParameters;
+parcelable EuiccRulesAuthTable;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java
new file mode 100644
index 0000000..7efe043
--- /dev/null
+++ b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.euicc;
+
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.service.carrier.CarrierIdentifier;
+import android.service.euicc.EuiccProfileInfo;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+
+/**
+ * This represents the RAT (Rules Authorisation Table) stored on eUICC.
+ *
+ * @hide
+ *
+ * TODO(b/35851809): Make this a @SystemApi.
+ */
+public final class EuiccRulesAuthTable implements Parcelable {
+ /** Profile policy rule flags */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "POLICY_RULE_FLAG_" }, value = {
+ POLICY_RULE_FLAG_CONSENT_REQUIRED
+ })
+ public @interface PolicyRuleFlag {}
+
+ /** User consent is required to install the profile. */
+ public static final int POLICY_RULE_FLAG_CONSENT_REQUIRED = 1;
+
+ private final int[] mPolicyRules;
+ private final CarrierIdentifier[][] mCarrierIds;
+ private final int[] mPolicyRuleFlags;
+
+ /** This is used to build new {@link EuiccRulesAuthTable} instance. */
+ public static final class Builder {
+ private int[] mPolicyRules;
+ private CarrierIdentifier[][] mCarrierIds;
+ private int[] mPolicyRuleFlags;
+ private int mPosition;
+
+ /**
+ * Creates a new builder.
+ *
+ * @param ruleNum The number of authorisation rules in the table.
+ */
+ public Builder(int ruleNum) {
+ mPolicyRules = new int[ruleNum];
+ mCarrierIds = new CarrierIdentifier[ruleNum][];
+ mPolicyRuleFlags = new int[ruleNum];
+ }
+
+ /**
+ * Builds the RAT instance. This builder should not be used anymore after this method is
+ * called, otherwise {@link NullPointerException} will be thrown.
+ */
+ public EuiccRulesAuthTable build() {
+ if (mPosition != mPolicyRules.length) {
+ throw new IllegalStateException(
+ "Not enough rules are added, expected: "
+ + mPolicyRules.length
+ + ", added: "
+ + mPosition);
+ }
+ return new EuiccRulesAuthTable(mPolicyRules, mCarrierIds, mPolicyRuleFlags);
+ }
+
+ /**
+ * Adds an authorisation rule.
+ *
+ * @throws ArrayIndexOutOfBoundsException If the {@code mPosition} is larger than the size
+ * this table.
+ */
+ public Builder add(int policyRules, CarrierIdentifier[] carrierId, int policyRuleFlags) {
+ if (mPosition >= mPolicyRules.length) {
+ throw new ArrayIndexOutOfBoundsException(mPosition);
+ }
+ mPolicyRules[mPosition] = policyRules;
+ mCarrierIds[mPosition] = carrierId;
+ mPolicyRuleFlags[mPosition] = policyRuleFlags;
+ mPosition++;
+ return this;
+ }
+ }
+
+ /**
+ * @param mccRule A 2-character or 3-character string which can be either MCC or MNC. The
+ * character 'E' is used as a wild char to match any digit.
+ * @param mcc A 2-character or 3-character string which can be either MCC or MNC.
+ * @return Whether the {@code mccRule} matches {@code mcc}.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static boolean match(String mccRule, String mcc) {
+ if (mccRule.length() < mcc.length()) {
+ return false;
+ }
+ for (int i = 0; i < mccRule.length(); i++) {
+ // 'E' is the wild char to match any digit.
+ if (mccRule.charAt(i) == 'E'
+ || (i < mcc.length() && mccRule.charAt(i) == mcc.charAt(i))) {
+ continue;
+ }
+ return false;
+ }
+ return true;
+ }
+
+ private EuiccRulesAuthTable(int[] policyRules, CarrierIdentifier[][] carrierIds,
+ int[] policyRuleFlags) {
+ mPolicyRules = policyRules;
+ mCarrierIds = carrierIds;
+ mPolicyRuleFlags = policyRuleFlags;
+ }
+
+ /**
+ * Finds the index of the first authorisation rule matching the given policy and carrier id. If
+ * the returned index is not negative, the carrier is allowed to apply this policy to its
+ * profile.
+ *
+ * @param policy The policy rule.
+ * @param carrierId The carrier id.
+ * @return The index of authorization rule. If no rule is found, -1 will be returned.
+ */
+ public int findIndex(@EuiccProfileInfo.PolicyRule int policy, CarrierIdentifier carrierId) {
+ for (int i = 0; i < mPolicyRules.length; i++) {
+ if ((mPolicyRules[i] & policy) == 0) {
+ continue;
+ }
+ CarrierIdentifier[] carrierIds = mCarrierIds[i];
+ if (carrierIds == null || carrierIds.length == 0) {
+ continue;
+ }
+ for (int j = 0; j < carrierIds.length; j++) {
+ CarrierIdentifier ruleCarrierId = carrierIds[j];
+ if (!match(ruleCarrierId.getMcc(), carrierId.getMcc())
+ || !match(ruleCarrierId.getMnc(), carrierId.getMnc())) {
+ continue;
+ }
+ String gid = ruleCarrierId.getGid1();
+ if (!TextUtils.isEmpty(gid) && !gid.equals(carrierId.getGid1())) {
+ continue;
+ }
+ gid = ruleCarrierId.getGid2();
+ if (!TextUtils.isEmpty(gid) && !gid.equals(carrierId.getGid2())) {
+ continue;
+ }
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Tests if the entry in the table has the given policy rule flag.
+ *
+ * @param index The index of the entry.
+ * @param flag The policy rule flag to be tested.
+ * @throws ArrayIndexOutOfBoundsException If the {@code index} is negative or larger than the
+ * size of this table.
+ */
+ public boolean hasPolicyRuleFlag(int index, @PolicyRuleFlag int flag) {
+ if (index < 0 || index >= mPolicyRules.length) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ return (mPolicyRuleFlags[index] & flag) != 0;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeIntArray(mPolicyRules);
+ for (CarrierIdentifier[] ids : mCarrierIds) {
+ dest.writeTypedArray(ids, flags);
+ }
+ dest.writeIntArray(mPolicyRuleFlags);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+
+ EuiccRulesAuthTable that = (EuiccRulesAuthTable) obj;
+ if (mCarrierIds.length != that.mCarrierIds.length) {
+ return false;
+ }
+ for (int i = 0; i < mCarrierIds.length; i++) {
+ CarrierIdentifier[] carrierIds = mCarrierIds[i];
+ CarrierIdentifier[] thatCarrierIds = that.mCarrierIds[i];
+ if (carrierIds != null && thatCarrierIds != null) {
+ if (carrierIds.length != thatCarrierIds.length) {
+ return false;
+ }
+ for (int j = 0; j < carrierIds.length; j++) {
+ if (!carrierIds[j].equals(thatCarrierIds[j])) {
+ return false;
+ }
+ }
+ continue;
+ } else if (carrierIds == null && thatCarrierIds == null) {
+ continue;
+ }
+ return false;
+ }
+
+ return Arrays.equals(mPolicyRules, that.mPolicyRules)
+ && Arrays.equals(mPolicyRuleFlags, that.mPolicyRuleFlags);
+ }
+
+ private EuiccRulesAuthTable(Parcel source) {
+ mPolicyRules = source.createIntArray();
+ int len = mPolicyRules.length;
+ mCarrierIds = new CarrierIdentifier[len][];
+ for (int i = 0; i < len; i++) {
+ mCarrierIds[i] = source.createTypedArray(CarrierIdentifier.CREATOR);
+ }
+ mPolicyRuleFlags = source.createIntArray();
+ }
+
+ public static final Creator<EuiccRulesAuthTable> CREATOR =
+ new Creator<EuiccRulesAuthTable>() {
+ @Override
+ public EuiccRulesAuthTable createFromParcel(Parcel source) {
+ return new EuiccRulesAuthTable(source);
+ }
+
+ @Override
+ public EuiccRulesAuthTable[] newArray(int size) {
+ return new EuiccRulesAuthTable[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index 8230eaf..aaa0f08 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -26,12 +26,14 @@
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MMTelFeature;
import android.telephony.ims.feature.RcsFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.Log;
import android.util.SparseArray;
import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.ims.internal.IImsMMTelFeature;
import com.android.ims.internal.IImsRcsFeature;
+import com.android.ims.internal.IImsRegistration;
import com.android.ims.internal.IImsServiceController;
import com.android.internal.annotations.VisibleForTesting;
@@ -113,6 +115,12 @@
throws RemoteException {
ImsService.this.removeImsFeature(slotId, featureType, c);
}
+
+ @Override
+ public IImsRegistration getRegistration(int slotId) throws RemoteException {
+ ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
+ return r != null ? r.getBinder() : null;
+ }
};
/**
@@ -174,6 +182,8 @@
f.setSlotId(slotId);
f.addImsFeatureStatusCallback(c);
addImsFeature(slotId, featureType, f);
+ // TODO: Remove once new onFeatureReady AIDL is merged in.
+ f.onFeatureReady();
}
private void addImsFeature(int slotId, int featureType, ImsFeature f) {
@@ -236,4 +246,13 @@
public @Nullable RcsFeature onCreateRcsFeature(int slotId) {
return null;
}
+
+ /**
+ * @param slotId The slot that is associated with the IMS Registration.
+ * @return the ImsRegistration implementation associated with the slot.
+ * @hide
+ */
+ public ImsRegistrationImplBase getRegistration(int slotId) {
+ return new ImsRegistrationImplBase();
+ }
}
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index ca4a210..d47cea30 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -96,7 +96,7 @@
new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
private @ImsState int mState = STATE_NOT_AVAILABLE;
private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
- private Context mContext;
+ protected Context mContext;
public void setContext(Context context) {
mContext = context;
diff --git a/telephony/java/android/telephony/ims/internal/ImsService.java b/telephony/java/android/telephony/ims/internal/ImsService.java
index b7c8ca0..afaf332 100644
--- a/telephony/java/android/telephony/ims/internal/ImsService.java
+++ b/telephony/java/android/telephony/ims/internal/ImsService.java
@@ -24,7 +24,6 @@
import android.telephony.ims.internal.aidl.IImsConfig;
import android.telephony.ims.internal.aidl.IImsMmTelFeature;
import android.telephony.ims.internal.aidl.IImsRcsFeature;
-import android.telephony.ims.internal.aidl.IImsRegistration;
import android.telephony.ims.internal.aidl.IImsServiceController;
import android.telephony.ims.internal.aidl.IImsServiceControllerListener;
import android.telephony.ims.internal.feature.ImsFeature;
@@ -32,11 +31,12 @@
import android.telephony.ims.internal.feature.RcsFeature;
import android.telephony.ims.internal.stub.ImsConfigImplBase;
import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
-import android.telephony.ims.internal.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.Log;
import android.util.SparseArray;
import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsRegistration;
import com.android.internal.annotations.VisibleForTesting;
/**
diff --git a/telephony/java/android/telephony/ims/internal/SmsImplBase.java b/telephony/java/android/telephony/ims/internal/SmsImplBase.java
index 47414cf..eb805a8 100644
--- a/telephony/java/android/telephony/ims/internal/SmsImplBase.java
+++ b/telephony/java/android/telephony/ims/internal/SmsImplBase.java
@@ -124,70 +124,79 @@
* method should be implemented by the IMS providers to provide implementation of sending an SMS
* over IMS.
*
- * @param smsc the Short Message Service Center address.
- * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
- * {@link SmsMessage#FORMAT_3GPP2}.
+ * @param token unique token generated by the platform that should be used when triggering
+ * callbacks for this specific message.
* @param messageRef the message reference.
+ * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
+ * @param smsc the Short Message Service Center address.
+ * {@link SmsMessage#FORMAT_3GPP2}.
* @param isRetry whether it is a retry of an already attempted message or not.
* @param pdu PDUs representing the contents of the message.
*/
- public void sendSms(int messageRef, String format, String smsc, boolean isRetry, byte[] pdu) {
+ public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
+ byte[] pdu) {
// Base implementation returns error. Should be overridden.
try {
- onSendSmsResult(messageRef, SEND_STATUS_ERROR, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
+ onSendSmsResult(token, messageRef, SEND_STATUS_ERROR,
+ SmsManager.RESULT_ERROR_GENERIC_FAILURE);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Can not send sms: " + e.getMessage());
}
}
/**
- * This method will be triggered by the platform after {@link #onSmsReceived(String, byte[])} has
- * been called to deliver the result to the IMS provider.
+ * This method will be triggered by the platform after {@link #onSmsReceived(int, String, byte[])}
+ * has been called to deliver the result to the IMS provider.
*
+ * @param token token provided in {@link #onSmsReceived(int, String, byte[])}
* @param result result of delivering the message. Valid values are defined in
* {@link DeliverStatusResult}
- * @param messageRef the message reference or -1 of unavailable.
+ * @param messageRef the message reference
*/
- public void acknowledgeSms(int messageRef, @DeliverStatusResult int result) {
+ public void acknowledgeSms(int token, int messageRef, @DeliverStatusResult int result) {
}
/**
* This method will be triggered by the platform after
- * {@link #onSmsStatusReportReceived(int, int, byte[])} has been called to provide the result to
- * the IMS provider.
+ * {@link #onSmsStatusReportReceived(int, int, String, byte[])} has been called to provide the
+ * result to the IMS provider.
*
+ * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
* @param result result of delivering the message. Valid values are defined in
* {@link StatusReportResult}
- * @param messageRef the message reference or -1 of unavailable.
+ * @param messageRef the message reference
*/
- public void acknowledgeSmsReport(int messageRef, @StatusReportResult int result) {
+ public void acknowledgeSmsReport(int token, int messageRef, @StatusReportResult int result) {
}
/**
* This method should be triggered by the IMS providers when there is an incoming message. The
* platform will deliver the message to the messages database and notify the IMS provider of the
- * result by calling {@link #acknowledgeSms(int, int)}.
+ * result by calling {@link #acknowledgeSms(int, int, int)}.
*
* This method must not be called before {@link MmTelFeature#onFeatureReady()} is called.
*
+ * @param token unique token generated by IMS providers that the platform will use to trigger
+ * callbacks for this message.
* @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
* {@link SmsMessage#FORMAT_3GPP2}.
* @param pdu PDUs representing the contents of the message.
* @throws IllegalStateException if called before {@link MmTelFeature#onFeatureReady()}
*/
- public final void onSmsReceived(String format, byte[] pdu) throws IllegalStateException {
+ public final void onSmsReceived(int token, String format, byte[] pdu)
+ throws IllegalStateException {
synchronized (mLock) {
if (mListener == null) {
throw new IllegalStateException("Feature not ready.");
}
try {
- mListener.onSmsReceived(format, pdu);
- acknowledgeSms(-1, DELIVER_STATUS_OK);
+ mListener.onSmsReceived(token, format, pdu);
+ acknowledgeSms(token, 0, DELIVER_STATUS_OK);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage());
- acknowledgeSms(-1, DELIVER_STATUS_ERROR);
+ acknowledgeSms(token, 0, DELIVER_STATUS_ERROR);
}
}
}
@@ -198,6 +207,7 @@
*
* This method must not be called before {@link MmTelFeature#onFeatureReady()} is called.
*
+ * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
* @param messageRef the message reference. Should be between 0 and 255 per TS.123.040
* @param status result of sending the SMS. Valid values are defined in {@link SendStatusResult}
* @param reason reason in case status is failure. Valid values are:
@@ -213,35 +223,37 @@
* @throws RemoteException if the connection to the framework is not available. If this happens
* attempting to send the SMS should be aborted.
*/
- public final void onSendSmsResult(int messageRef, @SendStatusResult int status, int reason)
- throws IllegalStateException, RemoteException {
+ public final void onSendSmsResult(int token, int messageRef, @SendStatusResult int status,
+ int reason) throws IllegalStateException, RemoteException {
synchronized (mLock) {
if (mListener == null) {
throw new IllegalStateException("Feature not ready.");
}
- mListener.onSendSmsResult(messageRef, status, reason);
+ mListener.onSendSmsResult(token, messageRef, status, reason);
}
}
/**
* Sets the status report of the sent message.
*
+ * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
* @param messageRef the message reference.
* @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
* {@link SmsMessage#FORMAT_3GPP2}.
* @param pdu PDUs representing the content of the status report.
* @throws IllegalStateException if called before {@link MmTelFeature#onFeatureReady()}
*/
- public final void onSmsStatusReportReceived(int messageRef, String format, byte[] pdu) {
+ public final void onSmsStatusReportReceived(int token, int messageRef, String format,
+ byte[] pdu) {
synchronized (mLock) {
if (mListener == null) {
throw new IllegalStateException("Feature not ready.");
}
try {
- mListener.onSmsStatusReportReceived(messageRef, format, pdu);
+ mListener.onSmsStatusReportReceived(token, messageRef, format, pdu);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
- acknowledgeSmsReport(messageRef, STATUS_REPORT_STATUS_ERROR);
+ acknowledgeSmsReport(token, messageRef, STATUS_REPORT_STATUS_ERROR);
}
}
}
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl
index d976686..785113f 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl
@@ -52,8 +52,9 @@
IImsCapabilityCallback c);
// SMS APIs
void setSmsListener(IImsSmsListener l);
- oneway void sendSms(int messageRef, String format, String smsc, boolean retry, in byte[] pdu);
- oneway void acknowledgeSms(int messageRef, int result);
- oneway void acknowledgeSmsReport(int messageRef, int result);
+ oneway void sendSms(in int token, int messageRef, String format, String smsc, boolean retry,
+ in byte[] pdu);
+ oneway void acknowledgeSms(int token, int messageRef, int result);
+ oneway void acknowledgeSmsReport(int token, int messageRef, int result);
String getSmsFormat();
}
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl
index 8332bc0..43f5098 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl
@@ -24,4 +24,5 @@
*/
oneway interface IImsMmTelListener {
void onIncomingCall(IImsCallSession c);
+ void onVoiceMessageCountUpdate(int count);
}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl
index 8afb955..82a8525 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl
@@ -18,12 +18,12 @@
import android.telephony.ims.internal.aidl.IImsMmTelFeature;
import android.telephony.ims.internal.aidl.IImsRcsFeature;
-import android.telephony.ims.internal.aidl.IImsRegistration;
import android.telephony.ims.internal.aidl.IImsConfig;
import android.telephony.ims.internal.aidl.IImsServiceControllerListener;
import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsRegistration;
/**
* See ImsService and MmTelFeature for more information.
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsSmsListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsSmsListener.aidl
index 468629a..bf8d90b 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsSmsListener.aidl
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsSmsListener.aidl
@@ -21,7 +21,8 @@
* {@hide}
*/
interface IImsSmsListener {
- void onSendSmsResult(in int messageRef, in int status, in int reason);
- void onSmsStatusReportReceived(in int messageRef, in String format, in byte[] pdu);
- void onSmsReceived(in String format, in byte[] pdu);
+ void onSendSmsResult(in int token, in int messageRef, in int status, in int reason);
+ void onSmsStatusReportReceived(in int token, in int messageRef, in String format,
+ in byte[] pdu);
+ void onSmsReceived(in int token, in String format, in byte[] pdu);
}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
index 4d18873..5dbf077 100644
--- a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
+++ b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
@@ -18,7 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.ims.internal.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.ArraySet;
import java.util.ArrayList;
diff --git a/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
index 2f350c8..8d888c2 100644
--- a/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
@@ -28,8 +28,8 @@
import android.telephony.ims.internal.aidl.IImsCapabilityCallback;
import android.telephony.ims.internal.aidl.IImsMmTelFeature;
import android.telephony.ims.internal.aidl.IImsMmTelListener;
-import android.telephony.ims.internal.stub.ImsRegistrationImplBase;
import android.telephony.ims.internal.aidl.IImsSmsListener;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.ImsEcbmImplBase;
import android.telephony.ims.stub.ImsMultiEndpointImplBase;
import android.telephony.ims.stub.ImsUtImplBase;
@@ -154,23 +154,24 @@
}
@Override
- public void sendSms(int messageRef, String format, String smsc, boolean retry, byte[] pdu) {
+ public void sendSms(int token, int messageRef, String format, String smsc, boolean retry,
+ byte[] pdu) {
synchronized (mLock) {
- MmTelFeature.this.sendSms(messageRef, format, smsc, retry, pdu);
+ MmTelFeature.this.sendSms(token, messageRef, format, smsc, retry, pdu);
}
}
@Override
- public void acknowledgeSms(int messageRef, int result) {
+ public void acknowledgeSms(int token, int messageRef, int result) {
synchronized (mLock) {
- MmTelFeature.this.acknowledgeSms(messageRef, result);
+ MmTelFeature.this.acknowledgeSms(token, messageRef, result);
}
}
@Override
- public void acknowledgeSmsReport(int messageRef, int result) {
+ public void acknowledgeSmsReport(int token, int messageRef, int result) {
synchronized (mLock) {
- MmTelFeature.this.acknowledgeSmsReport(messageRef, result);
+ MmTelFeature.this.acknowledgeSmsReport(token, messageRef, result);
}
}
@@ -261,6 +262,15 @@
}
/**
+ * Updates the Listener when the voice message count for IMS has changed.
+ * @param count an integer representing the new message count.
+ */
+ @Override
+ public void onVoiceMessageCountUpdate(int count) {
+
+ }
+
+ /**
* Called when the IMS provider receives an incoming call.
* @param c The {@link ImsCallSession} associated with the new call.
*/
@@ -447,16 +457,17 @@
// Base Implementation - Should be overridden
}
- private void sendSms(int messageRef, String format, String smsc, boolean isRetry, byte[] pdu) {
- getSmsImplementation().sendSms(messageRef, format, smsc, isRetry, pdu);
+ private void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
+ byte[] pdu) {
+ getSmsImplementation().sendSms(token, messageRef, format, smsc, isRetry, pdu);
}
- private void acknowledgeSms(int messageRef, @DeliverStatusResult int result) {
- getSmsImplementation().acknowledgeSms(messageRef, result);
+ private void acknowledgeSms(int token, int messageRef, @DeliverStatusResult int result) {
+ getSmsImplementation().acknowledgeSms(token, messageRef, result);
}
- private void acknowledgeSmsReport(int messageRef, @StatusReportResult int result) {
- getSmsImplementation().acknowledgeSmsReport(messageRef, result);
+ private void acknowledgeSmsReport(int token, int messageRef, @StatusReportResult int result) {
+ getSmsImplementation().acknowledgeSmsReport(token, messageRef, result);
}
private String getSmsFormat() {
diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
similarity index 83%
rename from telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java
rename to telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 558b009..42af083 100644
--- a/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -14,16 +14,19 @@
* limitations under the License
*/
-package android.telephony.ims.internal.stub;
+package android.telephony.ims.stub;
import android.annotation.IntDef;
+import android.net.Uri;
+import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
-import android.telephony.ims.internal.aidl.IImsRegistration;
-import android.telephony.ims.internal.aidl.IImsRegistrationCallback;
import android.util.Log;
import com.android.ims.ImsReasonInfo;
+import com.android.ims.internal.IImsRegistration;
+import com.android.ims.internal.IImsRegistrationCallback;
+import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -62,23 +65,25 @@
// Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current
// state.
+ // The unknown state is set as the initialization state. This is so that we do not call back
+ // with NOT_REGISTERED in the case where the ImsService has not updated the registration state
+ // yet.
+ private static final int REGISTRATION_STATE_UNKNOWN = -1;
private static final int REGISTRATION_STATE_NOT_REGISTERED = 0;
private static final int REGISTRATION_STATE_REGISTERING = 1;
private static final int REGISTRATION_STATE_REGISTERED = 2;
-
/**
* Callback class for receiving Registration callback events.
+ * @hide
*/
- public static class Callback extends IImsRegistrationCallback.Stub {
-
+ public static class Callback {
/**
* Notifies the framework when the IMS Provider is connected to the IMS network.
*
* @param imsRadioTech the radio access technology. Valid values are defined in
* {@link ImsRegistrationTech}.
*/
- @Override
public void onRegistered(@ImsRegistrationTech int imsRadioTech) {
}
@@ -88,7 +93,6 @@
* @param imsRadioTech the radio access technology. Valid values are defined in
* {@link ImsRegistrationTech}.
*/
- @Override
public void onRegistering(@ImsRegistrationTech int imsRadioTech) {
}
@@ -97,7 +101,6 @@
*
* @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
*/
- @Override
public void onDeregistered(ImsReasonInfo info) {
}
@@ -108,10 +111,19 @@
* @param imsRadioTech The {@link ImsRegistrationTech} type that has failed
* @param info A {@link ImsReasonInfo} that identifies the reason for failure.
*/
- @Override
public void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
ImsReasonInfo info) {
}
+
+ /**
+ * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when
+ * it changes.
+ * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
+ * subscription.
+ */
+ public void onSubscriberAssociatedUriChanged(Uri[] uris) {
+
+ }
}
private final IImsRegistration mBinder = new IImsRegistration.Stub() {
@@ -139,9 +151,9 @@
private @ImsRegistrationTech
int mConnectionType = REGISTRATION_TECH_NONE;
// Locked on mLock
- private int mRegistrationState = REGISTRATION_STATE_NOT_REGISTERED;
- // Locked on mLock
- private ImsReasonInfo mLastDisconnectCause;
+ private int mRegistrationState = REGISTRATION_STATE_UNKNOWN;
+ // Locked on mLock, create unspecified disconnect cause.
+ private ImsReasonInfo mLastDisconnectCause = new ImsReasonInfo();
public final IImsRegistration getBinder() {
return mBinder;
@@ -221,6 +233,17 @@
});
}
+ public final void onSubscriberAssociatedUriChanged(Uri[] uris) {
+ mCallbacks.broadcast((c) -> {
+ try {
+ c.onSubscriberAssociatedUriChanged(uris);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, e + " " + "onSubscriberAssociatedUriChanged() - Skipping " +
+ "callback.");
+ }
+ });
+ }
+
private void updateToState(@ImsRegistrationTech int connType, int newState) {
synchronized (mLock) {
mConnectionType = connType;
@@ -241,7 +264,8 @@
}
}
- private @ImsRegistrationTech int getConnectionType() {
+ @VisibleForTesting
+ public final @ImsRegistrationTech int getConnectionType() {
synchronized (mLock) {
return mConnectionType;
}
@@ -271,6 +295,10 @@
c.onRegistered(getConnectionType());
break;
}
+ case REGISTRATION_STATE_UNKNOWN: {
+ // Do not callback if the state has not been updated yet by the ImsService.
+ break;
+ }
}
}
}
diff --git a/telephony/java/com/android/ims/ImsCallProfile.java b/telephony/java/com/android/ims/ImsCallProfile.java
index 489c208..693aaff 100644
--- a/telephony/java/com/android/ims/ImsCallProfile.java
+++ b/telephony/java/com/android/ims/ImsCallProfile.java
@@ -351,7 +351,7 @@
mServiceType = in.readInt();
mCallType = in.readInt();
mCallExtras = in.readBundle();
- mMediaProfile = in.readParcelable(null);
+ mMediaProfile = in.readParcelable(ImsStreamMediaProfile.class.getClassLoader());
}
public static final Creator<ImsCallProfile> CREATOR = new Creator<ImsCallProfile>() {
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl b/telephony/java/com/android/ims/internal/IImsRegistration.aidl
similarity index 82%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl
rename to telephony/java/com/android/ims/internal/IImsRegistration.aidl
index 687b7ca..6de264e 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRegistration.aidl
@@ -15,10 +15,9 @@
*/
-package android.telephony.ims.internal.aidl;
+package com.android.ims.internal;
-import android.telephony.ims.internal.aidl.IImsRegistrationCallback;
-import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+import com.android.ims.internal.IImsRegistrationCallback;
/**
* See ImsRegistration for more information.
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl b/telephony/java/com/android/ims/internal/IImsRegistrationCallback.aidl
similarity index 89%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl
rename to telephony/java/com/android/ims/internal/IImsRegistrationCallback.aidl
index a50575b..5f21167 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRegistrationCallback.aidl
@@ -15,8 +15,9 @@
*/
-package android.telephony.ims.internal.aidl;
+package com.android.ims.internal;
+import android.net.Uri;
import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
import com.android.ims.ImsReasonInfo;
@@ -31,4 +32,5 @@
void onRegistering(int imsRadioTech);
void onDeregistered(in ImsReasonInfo info);
void onTechnologyChangeFailed(int imsRadioTech, in ImsReasonInfo info);
+ void onSubscriberAssociatedUriChanged(in Uri[] uris);
}
\ No newline at end of file
diff --git a/telephony/java/com/android/ims/internal/IImsServiceController.aidl b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
index 857089f..7ac25ac 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceController.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
@@ -18,6 +18,7 @@
import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.ims.internal.IImsMMTelFeature;
+import com.android.ims.internal.IImsRegistration;
import com.android.ims.internal.IImsRcsFeature;
/**
@@ -29,4 +30,5 @@
IImsMMTelFeature createMMTelFeature(int slotId, in IImsFeatureStatusCallback c);
IImsRcsFeature createRcsFeature(int slotId, in IImsFeatureStatusCallback c);
void removeImsFeature(int slotId, int featureType, in IImsFeatureStatusCallback c);
+ IImsRegistration getRegistration(int slotId);
}
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index ac16139..8e3f4c0 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -46,5 +46,6 @@
void onVoiceActivationStateChanged(int activationState);
void onDataActivationStateChanged(int activationState);
void onCarrierNetworkChange(in boolean active);
+ void onUserMobileDataStateChanged(in boolean enabled);
}
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index 0f31821..f8a040d 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -152,6 +152,13 @@
in ImsiEncryptionInfo imsiEncryptionInfo);
/**
+ * Resets the Carrier Keys in the database. This involves 2 steps:
+ * 1. Delete the keys from the database.
+ * 2. Send an intent to download new Certificates.
+ */
+ void resetCarrierKeysForImsiEncryption(int subId, String callingPackage);
+
+ /**
* Retrieves the alpha identifier associated with the voice mail number.
*/
String getVoiceMailAlphaTag(String callingPackage);
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 416146f..fba82ee 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -40,6 +40,7 @@
import android.telephony.VisualVoicemailSmsFilterSettings;
import com.android.ims.internal.IImsMMTelFeature;
import com.android.ims.internal.IImsRcsFeature;
+import com.android.ims.internal.IImsRegistration;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.OperatorInfo;
@@ -808,6 +809,11 @@
IImsRcsFeature getRcsFeatureAndListen(int slotId, in IImsServiceFeatureCallback callback);
/**
+ * Returns the IImsRegistration associated with the slot and feature specified.
+ */
+ IImsRegistration getImsRegistration(int slotId, int feature);
+
+ /**
* Set the network selection mode to automatic.
*
* @param subId the id of the subscription to update.
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 75d8f3f..188167c 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -69,4 +69,5 @@
int activationState, int activationType);
void notifySubscriptionInfoChanged();
void notifyCarrierNetworkChange(in boolean active);
+ void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state);
}
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index f29d993c..51369d0 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -486,4 +486,10 @@
*/
public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE =
"com.android.omadm.service.CONFIGURATION_UPDATE";
+
+ /**
+ * Broadcast action to trigger the Carrier Certificate download.
+ */
+ public static final String ACTION_CARRIER_CERTIFICATE_DOWNLOAD =
+ "com.android.internal.telephony.ACTION_CARRIER_CERTIFICATE_DOWNLOAD";
}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl
similarity index 73%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl
index d750363..8a77bf1 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IAuthenticateServerCallback {
+ void onComplete(int resultCode, in byte[] response);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl
similarity index 73%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl
index d750363..f6b99e2 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface ICancelSessionCallback {
+ void onComplete(int resultCode, in byte[] response);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IDeleteProfileCallback.aidl
similarity index 75%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IDeleteProfileCallback.aidl
index d750363..23a642e 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IDeleteProfileCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IDeleteProfileCallback {
+ void onComplete(int resultCode);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IDisableProfileCallback.aidl
similarity index 71%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IDisableProfileCallback.aidl
index d750363..3ee0b3a 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IDisableProfileCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
+
+import android.service.euicc.EuiccProfileInfo;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IDisableProfileCallback {
+ void onComplete(int resultCode);
+}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
new file mode 100644
index 0000000..abc55c7
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.euicc;
+
+import com.android.internal.telephony.euicc.IGetAllProfilesCallback;
+import com.android.internal.telephony.euicc.IGetProfileCallback;
+import com.android.internal.telephony.euicc.IDisableProfileCallback;
+import com.android.internal.telephony.euicc.ISwitchToProfileCallback;
+import com.android.internal.telephony.euicc.ISetNicknameCallback;
+import com.android.internal.telephony.euicc.IDeleteProfileCallback;
+import com.android.internal.telephony.euicc.IResetMemoryCallback;
+import com.android.internal.telephony.euicc.IGetDefaultSmdpAddressCallback;
+import com.android.internal.telephony.euicc.IGetSmdsAddressCallback;
+import com.android.internal.telephony.euicc.ISetDefaultSmdpAddressCallback;
+import com.android.internal.telephony.euicc.IAuthenticateServerCallback;
+import com.android.internal.telephony.euicc.ICancelSessionCallback;
+import com.android.internal.telephony.euicc.IGetEuiccChallengeCallback;
+import com.android.internal.telephony.euicc.IGetEuiccInfo1Callback;
+import com.android.internal.telephony.euicc.IGetEuiccInfo2Callback;
+import com.android.internal.telephony.euicc.IGetRulesAuthTableCallback;
+import com.android.internal.telephony.euicc.IListNotificationsCallback;
+import com.android.internal.telephony.euicc.ILoadBoundProfilePackageCallback;
+import com.android.internal.telephony.euicc.IPrepareDownloadCallback;
+import com.android.internal.telephony.euicc.IRemoveNotificationFromListCallback;
+import com.android.internal.telephony.euicc.IRetrieveNotificationCallback;
+import com.android.internal.telephony.euicc.IRetrieveNotificationListCallback;
+
+/** @hide */
+interface IEuiccCardController {
+ oneway void getAllProfiles(String callingPackage, in IGetAllProfilesCallback callback);
+ oneway void getProfile(String callingPackage, String iccid, in IGetProfileCallback callback);
+ oneway void disableProfile(String callingPackage, String iccid, boolean refresh,
+ in IDisableProfileCallback callback);
+ oneway void switchToProfile(String callingPackage, String iccid, boolean refresh,
+ in ISwitchToProfileCallback callback);
+ String getEid();
+ oneway void setNickname(String callingPackage, String iccid, String nickname,
+ in ISetNicknameCallback callback);
+ oneway void deleteProfile(String callingPackage, String iccid,
+ in IDeleteProfileCallback callback);
+ oneway void resetMemory(String callingPackage, int options, in IResetMemoryCallback callback);
+ oneway void getDefaultSmdpAddress(String callingPackage,
+ in IGetDefaultSmdpAddressCallback callback);
+ oneway void getSmdsAddress(String callingPackage, in IGetSmdsAddressCallback callback);
+ oneway void setDefaultSmdpAddress(String callingPackage, String address,
+ in ISetDefaultSmdpAddressCallback callback);
+ oneway void getRulesAuthTable(String callingPackage, in IGetRulesAuthTableCallback callback);
+ oneway void getEuiccChallenge(String callingPackage, in IGetEuiccChallengeCallback callback);
+ oneway void getEuiccInfo1(String callingPackage, in IGetEuiccInfo1Callback callback);
+ oneway void getEuiccInfo2(String callingPackage, in IGetEuiccInfo2Callback callback);
+ oneway void authenticateServer(String callingPackage, String matchingId,
+ in byte[] serverSigned1, in byte[] serverSignature1, in byte[] euiccCiPkIdToBeUsed,
+ in byte[] serverCertificatein, in IAuthenticateServerCallback callback);
+ oneway void prepareDownload(String callingPackage, in byte[] hashCc, in byte[] smdpSigned2,
+ in byte[] smdpSignature2, in byte[] smdpCertificate, in IPrepareDownloadCallback callback);
+ oneway void loadBoundProfilePackage(String callingPackage, in byte[] boundProfilePackage,
+ in ILoadBoundProfilePackageCallback callback);
+ oneway void cancelSession(String callingPackage, in byte[] transactionId, int reason,
+ in ICancelSessionCallback callback);
+ oneway void listNotifications(String callingPackage, int events,
+ in IListNotificationsCallback callback);
+ oneway void retrieveNotificationList(String callingPackage, int events,
+ in IRetrieveNotificationListCallback callback);
+ oneway void retrieveNotification(String callingPackage, int seqNumber,
+ in IRetrieveNotificationCallback callback);
+ oneway void removeNotificationFromList(String callingPackage, int seqNumber,
+ in IRemoveNotificationFromListCallback callback);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetAllProfilesCallback.aidl
similarity index 68%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetAllProfilesCallback.aidl
index d750363..97b0768 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetAllProfilesCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
+
+import android.service.euicc.EuiccProfileInfo;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetAllProfilesCallback {
+ void onComplete(int resultCode, in EuiccProfileInfo[] profiles);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetDefaultSmdpAddressCallback.aidl
similarity index 73%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetDefaultSmdpAddressCallback.aidl
index d750363..79b659d 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetDefaultSmdpAddressCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetDefaultSmdpAddressCallback {
+ void onComplete(int resultCode, String address);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl
similarity index 73%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl
index d750363..5ffb340 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetEuiccChallengeCallback {
+ void onComplete(int resultCode, in byte[] challenge);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl
similarity index 74%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl
index d750363..9592acb 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetEuiccInfo1Callback {
+ void onComplete(int resultCode, in byte[] info);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl
similarity index 74%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl
index d750363..5256b35 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetEuiccInfo2Callback {
+ void onComplete(int resultCode, in byte[] info);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetProfileCallback.aidl
similarity index 69%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetProfileCallback.aidl
index d750363..e9fc9e9 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetProfileCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
+
+import android.service.euicc.EuiccProfileInfo;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetProfileCallback {
+ void onComplete(int resultCode, in EuiccProfileInfo profile);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl
similarity index 68%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl
index d750363..58f0bde 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
+
+import android.telephony.euicc.EuiccRulesAuthTable;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetRulesAuthTableCallback {
+ void onComplete(int resultCode, in EuiccRulesAuthTable rat);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetSmdsAddressCallback.aidl
similarity index 73%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetSmdsAddressCallback.aidl
index d750363..09a83aa 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetSmdsAddressCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetSmdsAddressCallback {
+ void onComplete(int resultCode, String address);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl
similarity index 67%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl
index d750363..65aa302 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
+
+import android.telephony.euicc.EuiccNotification;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IListNotificationsCallback {
+ void onComplete(int resultCode, in EuiccNotification[] notifications);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl
similarity index 72%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl
index d750363..4ad7081 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface ILoadBoundProfilePackageCallback {
+ void onComplete(int resultCode, in byte[] response);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl
similarity index 73%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl
index d750363..c035184 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IPrepareDownloadCallback {
+ void onComplete(int resultCode, in byte[] response);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl
similarity index 69%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl
index d750363..b22d0da 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
+
+import android.telephony.euicc.EuiccNotification;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IRemoveNotificationFromListCallback {
+ void onComplete(int resultCode);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IResetMemoryCallback.aidl
similarity index 75%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IResetMemoryCallback.aidl
index d750363..860c158 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IResetMemoryCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IResetMemoryCallback {
+ void onComplete(int resultCode);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl
similarity index 67%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl
index d750363..dd8889a9 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
+
+import android.telephony.euicc.EuiccNotification;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IRetrieveNotificationCallback {
+ void onComplete(int resultCode, in EuiccNotification notification);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl
similarity index 66%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl
index d750363..bc4e451 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
+
+import android.telephony.euicc.EuiccNotification;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IRetrieveNotificationListCallback {
+ void onComplete(int resultCode, in EuiccNotification[] notifications);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl
similarity index 74%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl
index d750363..1e47125 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface ISetDefaultSmdpAddressCallback {
+ void onComplete(int resultCode);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl
similarity index 75%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl
index d750363..5899980 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface ISetNicknameCallback {
+ void onComplete(int resultCode);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl
similarity index 68%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl
index d750363..21ff084 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
+
+import android.service.euicc.EuiccProfileInfo;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface ISwitchToProfileCallback {
+ void onComplete(int resultCode, in EuiccProfileInfo profile);
+}
diff --git a/test-base/Android.bp b/test-base/Android.bp
index 0088962..ccf57b0 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -49,7 +49,8 @@
// Build the repackaged.android.test.base library
// ==============================================
-// This contains repackaged versions of the classes from legacy-test.
+// This contains repackaged versions of the classes from
+// android.test.base.
java_library_static {
name: "repackaged.android.test.base",
@@ -97,7 +98,7 @@
],
static_libs: [
- "android.test.runner",
+ "android.test.runner-minus-junit",
"android.test.mock",
],
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 8eddec4..b1ae40e 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -24,7 +24,6 @@
no_framework_libs: true,
libs: [
"framework",
- "legacy-test",
],
}
diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
index 7207ebc..13e3693 100644
--- a/test-mock/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -1174,4 +1174,20 @@
public ArtManager getArtManager() {
throw new UnsupportedOperationException();
}
+
+ /**
+ * @hide
+ */
+ @Override
+ public void setHarmfulAppWarning(String packageName, CharSequence warning) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public CharSequence getHarmfulAppWarning(String packageName) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index 104ae82..dfaeed5 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -24,11 +24,28 @@
no_framework_libs: true,
libs: [
"framework",
- "legacy-test",
+ "android.test.base",
"android.test.mock",
],
}
+// Build the android.test.runner-minus-junit library
+// =================================================
+// This is provided solely for use by the legacy-android-test module.
+java_library {
+ name: "android.test.runner-minus-junit",
+
+ srcs: ["src/android/**/*.java"],
+
+ no_framework_libs: true,
+ libs: [
+ "framework",
+ "android.test.base",
+ "android.test.mock",
+ "junit",
+ ],
+}
+
// Build the repackaged.android.test.runner library
// ================================================
java_library_static {
diff --git a/tests/ActivityManagerPerfTests/README.txt b/tests/ActivityManagerPerfTests/README.txt
new file mode 100644
index 0000000..77e0e90
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/README.txt
@@ -0,0 +1,34 @@
+ActivityManagerPerfTests
+
+Performance tests for various ActivityManager components, e.g. Services, Broadcasts
+
+Command to run tests (not working yet, atest seems buggy)
+* atest .../frameworks/base/tests/ActivityManagerPerfTests
+* m ActivityManagerPerfTests ActivityManagerPerfTestsTestApp && \
+ adb install $OUT/data/app/ActivityManagerPerfTests/ActivityManagerPerfTests.apk && \
+ adb install $OUT/data/app/ActivityManagerPerfTestsTestApp/ActivityManagerPerfTestsTestApp.apk && \
+ adb shell am instrument -w \
+ com.android.frameworks.perftests.amtests/android.support.test.runner.AndroidJUnitRunner
+
+Overview
+* The numbers we are trying to measure are end-to-end numbers
+ * For example, the time it takes from sending an Intent to start a Service
+ to the time the Service runs its callbacks
+* System.nanoTime() is monotonic and consistent between processes, so we use that for measuring time
+* To make sure the test app is running, we start an Activity
+* If the test app is involved, it will measure the time and send it back to the instrumentation test
+ * The time is sent back through a Binder interface in the Intent
+ * Each sent time is tagged with an id since there can be multiple events that send back a time
+ * For example, one is sent when the Activity is started, and another could be sent when a
+ Broadcast is received
+
+Structure
+* tests
+ * Instrumentation test which runs the various performance tests and reports the results
+
+* test-app
+ * Target package which contains the Services, BroadcastReceivers, etc. to test against
+ * Sends the time it measures back to the test package
+
+* utils
+ * Utilities that both the instrumentation test and test app can use
diff --git a/tests/ActivityManagerPerfTests/test-app/Android.mk b/tests/ActivityManagerPerfTests/test-app/Android.mk
new file mode 100644
index 0000000..b0a5db7
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/test-app/Android.mk
@@ -0,0 +1,28 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ ActivityManagerPerfTestsUtils
+
+LOCAL_PACKAGE_NAME := ActivityManagerPerfTestsTestApp
+
+include $(BUILD_PACKAGE)
diff --git a/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml
new file mode 100644
index 0000000..7145110
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.perftests.amteststestapp">
+ <application android:name=".TestApplication">
+ <activity android:name=".TestActivity" android:exported="true"/>
+ <receiver
+ android:name=".TestBroadcastReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="com.android.frameworks.perftests.ACTION_BROADCAST_MANIFEST_RECEIVE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </receiver>
+ </application>
+</manifest>
diff --git a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestActivity.java b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestActivity.java
new file mode 100644
index 0000000..7ea9ba3
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestActivity.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.frameworks.perftests.amteststestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.frameworks.perftests.am.util.Constants;
+import com.android.frameworks.perftests.am.util.Utils;
+
+public class TestActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Utils.sendTime(getIntent(), Constants.TYPE_ACTIVITY_CREATED);
+ }
+}
diff --git a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestApplication.java b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestApplication.java
new file mode 100644
index 0000000..e26ffcd
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestApplication.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.frameworks.perftests.amteststestapp;
+
+import android.app.Application;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
+
+import com.android.frameworks.perftests.am.util.Constants;
+import com.android.frameworks.perftests.am.util.Utils;
+
+public class TestApplication extends Application {
+ private static final String TAG = TestApplication.class.getSimpleName();
+
+ @Override
+ public void onCreate() {
+ createRegisteredReceiver();
+
+ super.onCreate();
+ }
+
+ // Create registered BroadcastReceiver
+ private void createRegisteredReceiver() {
+ BroadcastReceiver registered = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, "RegisteredReceiver.onReceive");
+ Utils.sendTime(intent, Constants.TYPE_BROADCAST_RECEIVE);
+ }
+ };
+ IntentFilter intentFilter = new IntentFilter(Constants.ACTION_BROADCAST_REGISTERED_RECEIVE);
+ registerReceiver(registered, intentFilter);
+ }
+}
diff --git a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestBroadcastReceiver.java b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestBroadcastReceiver.java
new file mode 100644
index 0000000..336bf9d
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestBroadcastReceiver.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.frameworks.perftests.amteststestapp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import com.android.frameworks.perftests.am.util.Constants;
+import com.android.frameworks.perftests.am.util.Utils;
+
+public class TestBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Utils.sendTime(intent, Constants.TYPE_BROADCAST_RECEIVE);
+ }
+}
diff --git a/tests/ActivityManagerPerfTests/tests/Android.mk b/tests/ActivityManagerPerfTests/tests/Android.mk
new file mode 100644
index 0000000..daf603d
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/tests/Android.mk
@@ -0,0 +1,33 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ apct-perftests-utils \
+ ActivityManagerPerfTestsUtils
+
+LOCAL_PACKAGE_NAME := ActivityManagerPerfTests
+
+# For android.permission.FORCE_STOP_PACKAGES permission
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/tests/ActivityManagerPerfTests/tests/AndroidManifest.xml b/tests/ActivityManagerPerfTests/tests/AndroidManifest.xml
new file mode 100644
index 0000000..4e194c6
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/tests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.perftests.amtests">
+ <uses-permission android:name="android.permission.DUMP" />
+ <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.perftests.amtests"/>
+</manifest>
diff --git a/tests/ActivityManagerPerfTests/tests/AndroidTest.xml b/tests/ActivityManagerPerfTests/tests/AndroidTest.xml
new file mode 100644
index 0000000..ffb5404
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/tests/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs ActivityManager Performance Tests">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="ActivityManagerPerfTests.apk"/>
+ <option name="test-file-name" value="ActivityManagerPerfTestsTestApp.apk"/>
+ <option name="cleanup-apks" value="true"/>
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct"/>
+ <option name="test-tag" value="ActivityManagerPerfTests"/>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.frameworks.perftests.amtests"/>
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java
new file mode 100644
index 0000000..661abe9
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.frameworks.perftests.am.tests;
+
+import android.content.Context;
+import android.content.Intent;
+import android.perftests.utils.ManualBenchmarkState;
+import android.perftests.utils.PerfManualStatusReporter;
+import android.support.test.InstrumentationRegistry;
+
+import com.android.frameworks.perftests.am.util.TargetPackageUtils;
+import com.android.frameworks.perftests.am.util.TimeReceiver;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+
+import java.util.function.LongSupplier;
+
+public class BasePerfTest {
+ private static final String TAG = BasePerfTest.class.getSimpleName();
+
+ private TimeReceiver mTimeReceiver;
+
+ @Rule
+ public PerfManualStatusReporter mPerfManualStatusReporter = new PerfManualStatusReporter();
+
+ protected Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mTimeReceiver = new TimeReceiver();
+ }
+
+ @After
+ public void tearDown() {
+ TargetPackageUtils.killTargetPackage(mContext);
+ }
+
+ protected Intent createIntent(String action) {
+ final Intent intent = new Intent(action);
+ intent.addFlags(
+ Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+ intent.putExtras(mTimeReceiver.createReceiveTimeExtraBinder());
+ return intent;
+ }
+
+ private void setUpIteration() {
+ mTimeReceiver.clear();
+ TargetPackageUtils.killTargetPackage(mContext);
+ }
+
+ protected void startTargetPackage() {
+ TargetPackageUtils.startTargetPackage(mContext, mTimeReceiver);
+ }
+
+ protected long getReceivedTimeNs(String type) {
+ return mTimeReceiver.getReceivedTimeNs(type);
+ }
+
+ protected void runPerfFunction(LongSupplier func) {
+ final ManualBenchmarkState benchmarkState = mPerfManualStatusReporter.getBenchmarkState();
+ long elapsedTimeNs = 0;
+ while (benchmarkState.keepRunning(elapsedTimeNs)) {
+ setUpIteration();
+ elapsedTimeNs = func.getAsLong();
+ }
+ }
+}
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java
new file mode 100644
index 0000000..795f498
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.frameworks.perftests.am.tests;
+
+import android.content.Intent;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.perftests.am.util.Constants;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class BroadcastPerfTest extends BasePerfTest {
+ @Test
+ public void manifestBroadcastRunning() {
+ runPerfFunction(() -> {
+ startTargetPackage();
+
+ final Intent intent = createIntent(Constants.ACTION_BROADCAST_MANIFEST_RECEIVE);
+
+ final long startTime = System.nanoTime();
+
+ mContext.sendBroadcast(intent);
+
+ final long endTime = getReceivedTimeNs(Constants.TYPE_BROADCAST_RECEIVE);
+
+ return endTime - startTime;
+ });
+ }
+
+ @Test
+ public void manifestBroadcastNotRunning() {
+ runPerfFunction(() -> {
+ final Intent intent = createIntent(Constants.ACTION_BROADCAST_MANIFEST_RECEIVE);
+
+ final long startTime = System.nanoTime();
+
+ mContext.sendBroadcast(intent);
+
+ final long endTime = getReceivedTimeNs(Constants.TYPE_BROADCAST_RECEIVE);
+
+ return endTime - startTime;
+ });
+ }
+
+ @Test
+ public void registeredBroadcast() {
+ runPerfFunction(() -> {
+ startTargetPackage();
+
+ final Intent intent = createIntent(Constants.ACTION_BROADCAST_REGISTERED_RECEIVE);
+
+ final long startTime = System.nanoTime();
+
+ mContext.sendBroadcast(intent);
+
+ final long endTime = getReceivedTimeNs(Constants.TYPE_BROADCAST_RECEIVE);
+
+ return endTime - startTime;
+ });
+ }
+}
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java
new file mode 100644
index 0000000..c867141
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.frameworks.perftests.am.util;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.SystemClock;
+
+public class TargetPackageUtils {
+ private static final String TAG = TargetPackageUtils.class.getSimpleName();
+
+ public static final String PACKAGE_NAME = "com.android.frameworks.perftests.amteststestapp";
+ public static final String ACTIVITY_NAME = PACKAGE_NAME + ".TestActivity";
+
+ private static final long WAIT_TIME_MS = 100L;
+
+ // Cache for test app's uid, so we only have to query it once.
+ private static int sTestAppUid = -1;
+
+ /**
+ * Kills the test package synchronously.
+ */
+ public static void killTargetPackage(Context context) {
+ ActivityManager activityManager = context.getSystemService(ActivityManager.class);
+ activityManager.forceStopPackage(PACKAGE_NAME);
+ while (targetPackageIsRunning(context)) {
+ sleep();
+ }
+
+ Utils.drainBroadcastQueue();
+ }
+
+ /**
+ * Starts the test package synchronously. It does so by starting an Activity.
+ */
+ public static void startTargetPackage(Context context, TimeReceiver timeReceiver) {
+ // "am start-activity -W PACKAGE_NAME/ACTIVITY_CLASS_NAME" still requires a sleep even
+ // though it should be synchronous, so just use Intent instead
+ final Intent intent = new Intent();
+ intent.putExtras(timeReceiver.createReceiveTimeExtraBinder());
+ intent.setClassName(PACKAGE_NAME, ACTIVITY_NAME);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+
+ while (!targetPackageIsRunning(context)) {
+ sleep();
+ }
+ // make sure Application has run
+ timeReceiver.getReceivedTimeNs(Constants.TYPE_ACTIVITY_CREATED);
+ Utils.drainBroadcastQueue();
+ }
+
+ private static boolean targetPackageIsRunning(Context context) {
+ final int uid = getTestAppUid(context);
+ final String result = Utils.runShellCommand(
+ String.format("cmd activity get-uid-state %d", uid));
+ return !result.contains("(NONEXISTENT)");
+ }
+
+ private static void sleep() {
+ SystemClock.sleep(WAIT_TIME_MS);
+ }
+
+ private static int getTestAppUid(Context context) {
+ if (sTestAppUid == -1) {
+ final PackageManager pm = context.getPackageManager();
+ try {
+ sTestAppUid = pm.getPackageUid(PACKAGE_NAME, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return sTestAppUid;
+ }
+
+}
+
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TimeReceiver.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TimeReceiver.java
new file mode 100644
index 0000000..9cf6ee7
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TimeReceiver.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.frameworks.perftests.am.util;
+
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * TimeReceiver will listen for any messages containing a timestamp by starting a BroadcastReceiver
+ * which listens for Intents with the SendTime.ACTION_SEND_TIME action.
+ */
+public class TimeReceiver {
+ private static final String TAG = TimeReceiver.class.getSimpleName();
+ private static final long DEFAULT_RECEIVE_TIME_TIMEOUT_MILLIS = 10000L;
+
+ private BlockingQueue<ReceivedMessage> mQueue = new LinkedBlockingQueue<>();
+
+ private static class ReceivedMessage {
+ private final String mReceivedMessageType;
+ private final long mReceivedTimeNs;
+
+ public ReceivedMessage(String receivedMessageType, long receivedTimeNs) {
+ mReceivedMessageType = receivedMessageType;
+ mReceivedTimeNs = receivedTimeNs;
+ }
+ }
+
+ public Bundle createReceiveTimeExtraBinder() {
+ Bundle extras = new Bundle();
+ extras.putBinder(Constants.EXTRA_RECEIVER_CALLBACK, new ITimeReceiverCallback.Stub() {
+ @Override
+ public void sendTime(String type, long timeNs) throws RemoteException {
+ if (type == null) {
+ throw new RuntimeException("receivedType is null");
+ }
+ if (timeNs < 0) {
+ throw new RuntimeException(
+ "receivedTime is negative/non-existant: " + timeNs);
+ }
+ Log.i(TAG, type + " " + timeNs);
+ mQueue.add(new ReceivedMessage(type, timeNs));
+ }
+ });
+ return extras;
+ }
+
+ public long getReceivedTimeNs(String type) {
+ return getReceivedTimeNs(type, DEFAULT_RECEIVE_TIME_TIMEOUT_MILLIS);
+ }
+
+ /**
+ * Returns a received timestamp with the given type tag. Will throw away any messages with a
+ * different type tag. If it times out, a RuntimeException is thrown.
+ */
+ public long getReceivedTimeNs(String type, long timeoutMs) {
+ ReceivedMessage message;
+ long endTimeNs = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeoutMs);
+ do {
+ long curTimeNs = System.nanoTime();
+ if (curTimeNs > endTimeNs) {
+ throw new RuntimeException("Timed out when listening for a time: " + type);
+ }
+ try {
+ Log.i(TAG, "waiting for message " + type);
+ message = mQueue.poll(endTimeNs - curTimeNs, TimeUnit.NANOSECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ if (message == null) {
+ throw new RuntimeException("Timed out when listening for a time: " + type);
+ }
+ Log.i(TAG, "got message " + message.mReceivedMessageType);
+ if (!type.equals(message.mReceivedMessageType)) {
+ Log.i(TAG, String.format("Expected type \"%s\", got \"%s\" (%d), skipping", type,
+ message.mReceivedMessageType, message.mReceivedTimeNs));
+ }
+ } while (!type.equals(message.mReceivedMessageType));
+ return message.mReceivedTimeNs;
+ }
+
+ /**
+ * Clears the message queue.
+ */
+ public void clear() {
+ mQueue.clear();
+ }
+}
diff --git a/tests/ActivityManagerPerfTests/utils/Android.mk b/tests/ActivityManagerPerfTests/utils/Android.mk
new file mode 100644
index 0000000..7276e37
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/utils/Android.mk
@@ -0,0 +1,31 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ src/com/android/frameworks/perftests/am/util/ITimeReceiverCallback.aidl
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ junit \
+ ub-uiautomator
+
+LOCAL_MODULE := ActivityManagerPerfTestsUtils
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Constants.java b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Constants.java
new file mode 100644
index 0000000..6528028
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Constants.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.frameworks.perftests.am.util;
+
+public class Constants {
+ public static final String TYPE_ACTIVITY_CREATED = "activity_create";
+ public static final String TYPE_BROADCAST_RECEIVE = "broadcast_receive";
+
+ public static final String ACTION_BROADCAST_MANIFEST_RECEIVE =
+ "com.android.frameworks.perftests.ACTION_BROADCAST_MANIFEST_RECEIVE";
+ public static final String ACTION_BROADCAST_REGISTERED_RECEIVE =
+ "com.android.frameworks.perftests.ACTION_BROADCAST_REGISTERED_RECEIVE";
+
+ public static final String EXTRA_RECEIVER_CALLBACK = "receiver_callback_binder";
+}
diff --git a/core/java/android/security/recoverablekeystore/KeyDerivationParameters.aidl b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/ITimeReceiverCallback.aidl
similarity index 74%
copy from core/java/android/security/recoverablekeystore/KeyDerivationParameters.aidl
copy to tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/ITimeReceiverCallback.aidl
index fe13179..b43d49a 100644
--- a/core/java/android/security/recoverablekeystore/KeyDerivationParameters.aidl
+++ b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/ITimeReceiverCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-package android.security.recoverablekeystore;
+package com.android.frameworks.perftests.am.util;
-/* @hide */
-parcelable KeyDerivationParameters;
+interface ITimeReceiverCallback {
+ void sendTime(String type, long timeNs);
+}
\ No newline at end of file
diff --git a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java
new file mode 100644
index 0000000..493d8cd
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.frameworks.perftests.am.util;
+
+import android.content.Intent;
+import android.os.RemoteException;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+
+import java.io.IOException;
+
+public class Utils {
+ private static final String TAG = "AmPerfTestsUtils";
+
+ public static void drainBroadcastQueue() {
+ runShellCommand("am wait-for-broadcast-idle");
+ }
+
+ /**
+ * Runs the command and returns the stdout.
+ */
+ public static String runShellCommand(String cmd) {
+ try {
+ return UiDevice.getInstance(
+ InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Sends the current time in a message with the given type so TimeReceiver can receive it.
+ */
+ public static void sendTime(Intent intent, String type) {
+ final long time = System.nanoTime();
+ final ITimeReceiverCallback sendTimeBinder = ITimeReceiverCallback.Stub.asInterface(
+ intent.getExtras().getBinder(Constants.EXTRA_RECEIVER_CALLBACK));
+ try {
+ sendTimeBinder.sendTime(type, time);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.getMessage());
+ }
+ }
+}
diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java
index efc01f2a..f6c5532 100644
--- a/tests/net/java/android/net/IpSecConfigTest.java
+++ b/tests/net/java/android/net/IpSecConfigTest.java
@@ -36,19 +36,16 @@
public void testDefaults() throws Exception {
IpSecConfig c = new IpSecConfig();
assertEquals(IpSecTransform.MODE_TRANSPORT, c.getMode());
- assertEquals("", c.getLocalAddress());
- assertEquals("", c.getRemoteAddress());
+ assertEquals("", c.getSourceAddress());
+ assertEquals("", c.getDestinationAddress());
assertNull(c.getNetwork());
assertEquals(IpSecTransform.ENCAP_NONE, c.getEncapType());
assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getEncapSocketResourceId());
assertEquals(0, c.getEncapRemotePort());
assertEquals(0, c.getNattKeepaliveInterval());
- for (int direction :
- new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN}) {
- assertNull(c.getEncryption(direction));
- assertNull(c.getAuthentication(direction));
- assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId(direction));
- }
+ assertNull(c.getEncryption());
+ assertNull(c.getAuthentication());
+ assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId());
}
@Test
@@ -57,34 +54,21 @@
IpSecConfig c = new IpSecConfig();
c.setMode(IpSecTransform.MODE_TUNNEL);
- c.setLocalAddress("0.0.0.0");
- c.setRemoteAddress("1.2.3.4");
+ c.setSourceAddress("0.0.0.0");
+ c.setDestinationAddress("1.2.3.4");
c.setEncapType(android.system.OsConstants.UDP_ENCAP_ESPINUDP);
c.setEncapSocketResourceId(7);
c.setEncapRemotePort(22);
c.setNattKeepaliveInterval(42);
c.setEncryption(
- IpSecTransform.DIRECTION_OUT,
new IpSecAlgorithm(
IpSecAlgorithm.CRYPT_AES_CBC,
new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}));
c.setAuthentication(
- IpSecTransform.DIRECTION_OUT,
new IpSecAlgorithm(
IpSecAlgorithm.AUTH_HMAC_MD5,
new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0}));
- c.setSpiResourceId(IpSecTransform.DIRECTION_OUT, 1984);
- c.setEncryption(
- IpSecTransform.DIRECTION_IN,
- new IpSecAlgorithm(
- IpSecAlgorithm.CRYPT_AES_CBC,
- new byte[] {2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}));
- c.setAuthentication(
- IpSecTransform.DIRECTION_IN,
- new IpSecAlgorithm(
- IpSecAlgorithm.AUTH_HMAC_MD5,
- new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 1}));
- c.setSpiResourceId(IpSecTransform.DIRECTION_IN, 99);
+ c.setSpiResourceId(1984);
assertParcelingIsLossless(c);
}
diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java
index 0f40b45..cc3366f 100644
--- a/tests/net/java/android/net/IpSecManagerTest.java
+++ b/tests/net/java/android/net/IpSecManagerTest.java
@@ -81,15 +81,13 @@
IpSecSpiResponse spiResp =
new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI);
when(mMockIpSecService.allocateSecurityParameterIndex(
- eq(IpSecTransform.DIRECTION_IN),
eq(GOOGLE_DNS_4.getHostAddress()),
eq(DROID_SPI),
anyObject()))
.thenReturn(spiResp);
IpSecManager.SecurityParameterIndex droidSpi =
- mIpSecManager.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_IN, GOOGLE_DNS_4, DROID_SPI);
+ mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4, DROID_SPI);
assertEquals(DROID_SPI, droidSpi.getSpi());
droidSpi.close();
@@ -103,15 +101,13 @@
IpSecSpiResponse spiResp =
new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI);
when(mMockIpSecService.allocateSecurityParameterIndex(
- eq(IpSecTransform.DIRECTION_OUT),
eq(GOOGLE_DNS_4.getHostAddress()),
eq(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX),
anyObject()))
.thenReturn(spiResp);
IpSecManager.SecurityParameterIndex randomSpi =
- mIpSecManager.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
+ mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4);
assertEquals(DROID_SPI, randomSpi.getSpi());
@@ -124,16 +120,15 @@
* Throws resource unavailable exception
*/
@Test
- public void testAllocSpiResUnavaiableExeption() throws Exception {
+ public void testAllocSpiResUnavailableException() throws Exception {
IpSecSpiResponse spiResp =
new IpSecSpiResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE, 0, 0);
when(mMockIpSecService.allocateSecurityParameterIndex(
- anyInt(), anyString(), anyInt(), anyObject()))
+ anyString(), anyInt(), anyObject()))
.thenReturn(spiResp);
try {
- mIpSecManager.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
+ mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4);
fail("ResourceUnavailableException was not thrown");
} catch (IpSecManager.ResourceUnavailableException e) {
}
@@ -143,15 +138,14 @@
* Throws spi unavailable exception
*/
@Test
- public void testAllocSpiSpiUnavaiableExeption() throws Exception {
+ public void testAllocSpiSpiUnavailableException() throws Exception {
IpSecSpiResponse spiResp = new IpSecSpiResponse(IpSecManager.Status.SPI_UNAVAILABLE, 0, 0);
when(mMockIpSecService.allocateSecurityParameterIndex(
- anyInt(), anyString(), anyInt(), anyObject()))
+ anyString(), anyInt(), anyObject()))
.thenReturn(spiResp);
try {
- mIpSecManager.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
+ mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4);
fail("ResourceUnavailableException was not thrown");
} catch (IpSecManager.ResourceUnavailableException e) {
}
@@ -163,8 +157,7 @@
@Test
public void testRequestAllocInvalidSpi() throws Exception {
try {
- mIpSecManager.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4, 0);
+ mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4, 0);
fail("Able to allocate invalid spi");
} catch (IllegalArgumentException e) {
}
diff --git a/tests/net/java/android/net/LinkPropertiesTest.java b/tests/net/java/android/net/LinkPropertiesTest.java
index 52da79a..f3c22a5 100644
--- a/tests/net/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/java/android/net/LinkPropertiesTest.java
@@ -79,6 +79,9 @@
assertTrue(source.isIdenticalDnses(target));
assertTrue(target.isIdenticalDnses(source));
+ assertTrue(source.isIdenticalPrivateDns(target));
+ assertTrue(target.isIdenticalPrivateDns(source));
+
assertTrue(source.isIdenticalRoutes(target));
assertTrue(target.isIdenticalRoutes(source));
@@ -91,6 +94,9 @@
assertTrue(source.isIdenticalMtu(target));
assertTrue(target.isIdenticalMtu(source));
+ assertTrue(source.isIdenticalTcpBufferSizes(target));
+ assertTrue(target.isIdenticalTcpBufferSizes(source));
+
// Check result of equals().
assertTrue(source.equals(target));
assertTrue(target.equals(source));
diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
index 473dc538..9aad413 100644
--- a/tests/net/java/android/net/MacAddressTest.java
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -67,7 +67,7 @@
assertEquals(msg, t.expectedType, got);
if (got != MacAddress.TYPE_UNKNOWN) {
- assertEquals(got, MacAddress.fromBytes(t.addr).addressType());
+ assertEquals(got, MacAddress.fromBytes(t.addr).getAddressType());
}
}
}
@@ -191,7 +191,7 @@
assertTrue(stringRepr + " expected to be a locally assigned address",
mac.isLocallyAssigned());
- assertEquals(MacAddress.TYPE_UNICAST, mac.addressType());
+ assertEquals(MacAddress.TYPE_UNICAST, mac.getAddressType());
assertTrue(stringRepr + " expected to begin with " + expectedLocalOui,
stringRepr.startsWith(expectedLocalOui));
}
diff --git a/tests/net/java/android/net/NetworkTest.java b/tests/net/java/android/net/NetworkTest.java
index bacf986..94d01e9 100644
--- a/tests/net/java/android/net/NetworkTest.java
+++ b/tests/net/java/android/net/NetworkTest.java
@@ -147,9 +147,9 @@
// Adjust as necessary to test an implementation's specific constants.
// When running with runtest, "adb logcat -s TestRunner" can be useful.
- assertEquals(4311403230L, one.getNetworkHandle());
- assertEquals(8606370526L, two.getNetworkHandle());
- assertEquals(12901337822L, three.getNetworkHandle());
+ assertEquals(7700664333L, one.getNetworkHandle());
+ assertEquals(11995631629L, two.getNetworkHandle());
+ assertEquals(16290598925L, three.getNetworkHandle());
}
private static <T> void assertNotEqual(T t1, T t2) {
diff --git a/core/tests/coretests/src/android/net/NetworkUtilsTest.java b/tests/net/java/android/net/NetworkUtilsTest.java
similarity index 100%
rename from core/tests/coretests/src/android/net/NetworkUtilsTest.java
rename to tests/net/java/android/net/NetworkUtilsTest.java
diff --git a/core/tests/coretests/src/android/net/RouteInfoTest.java b/tests/net/java/android/net/RouteInfoTest.java
similarity index 100%
rename from core/tests/coretests/src/android/net/RouteInfoTest.java
rename to tests/net/java/android/net/RouteInfoTest.java
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
index 725ddb9..9b75a50 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/tests/net/java/android/net/apf/ApfTest.java
@@ -35,6 +35,7 @@
import android.net.ip.IpManager;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.RaEvent;
+import android.net.util.InterfaceParams;
import android.os.ConditionVariable;
import android.os.Parcelable;
import android.os.SystemClock;
@@ -62,7 +63,6 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
-import java.net.NetworkInterface;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Random;
@@ -635,7 +635,7 @@
public TestApfFilter(ApfConfiguration config, IpManager.Callback ipManagerCallback,
IpConnectivityLog log) throws Exception {
- super(config, NetworkInterface.getByName("lo"), ipManagerCallback, log);
+ super(config, InterfaceParams.getByName("lo"), ipManagerCallback, log);
}
// Pretend an RA packet has been received and show it to ApfFilter.
diff --git a/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java b/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java
index 54776db..e65585f 100644
--- a/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java
+++ b/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java
@@ -21,6 +21,7 @@
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.when;
+import android.net.util.InterfaceParams;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.Looper;
@@ -54,8 +55,8 @@
}
IpReachabilityMonitor makeMonitor() {
- return new IpReachabilityMonitor(
- "fake0", 1, mHandler, mLog, mCallback, null, mDependencies);
+ final InterfaceParams ifParams = new InterfaceParams("fake0", 1, null);
+ return new IpReachabilityMonitor(ifParams, mHandler, mLog, mCallback, null, mDependencies);
}
@Test
diff --git a/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java b/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
index 38d3d74..f9b7ec8 100644
--- a/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
+++ b/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import android.net.MacAddress;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.filters.SmallTest;
@@ -36,9 +37,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class ConnectivityPacketSummaryTest {
- private static final byte[] MYHWADDR = {
- asByte(0x80), asByte(0x7a), asByte(0xbf), asByte(0x6f), asByte(0x48), asByte(0xf3)
- };
+ private static final MacAddress MYHWADDR = MacAddress.fromString("80:7a:bf:6f:48:f3");
private String getSummary(String hexBytes) {
hexBytes = hexBytes.replaceAll("\\s+", "");
diff --git a/tests/net/java/android/net/util/InterfaceParamsTest.java b/tests/net/java/android/net/util/InterfaceParamsTest.java
new file mode 100644
index 0000000..21728af
--- /dev/null
+++ b/tests/net/java/android/net/util/InterfaceParamsTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 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.net.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class InterfaceParamsTest {
+ @Test
+ public void testNullInterfaceReturnsNull() {
+ assertNull(InterfaceParams.getByName(null));
+ }
+
+ @Test
+ public void testNonExistentInterfaceReturnsNull() {
+ assertNull(InterfaceParams.getByName("doesnotexist0"));
+ }
+
+ @Test
+ public void testLoopback() {
+ final InterfaceParams ifParams = InterfaceParams.getByName("lo");
+ assertNotNull(ifParams);
+ assertEquals("lo", ifParams.name);
+ assertTrue(ifParams.index > 0);
+ assertNotNull(ifParams.macAddr);
+ assertTrue(ifParams.defaultMtu >= NetworkConstants.ETHER_MTU);
+ }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 113cd37..b8e37f3 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -55,14 +55,20 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -116,6 +122,7 @@
import android.util.ArraySet;
import android.util.Log;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
@@ -132,6 +139,7 @@
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
@@ -174,8 +182,11 @@
@Mock IpConnectivityMetrics.Logger mMetricsService;
@Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
+ @Mock INetworkManagementService mNetworkManagementService;
@Mock INetworkStatsService mStatsService;
+ private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class);
+
// This class exists to test bindProcessToNetwork and getBoundNetworkForProcess. These methods
// do not go through ConnectivityService but talk to netd directly, so they don't automatically
// reflect the state of our test ConnectivityService.
@@ -872,7 +883,7 @@
LocalServices.addService(
NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class));
mService = new WrappedConnectivityService(mServiceContext,
- mock(INetworkManagementService.class),
+ mNetworkManagementService,
mStatsService,
mock(INetworkPolicyManager.class),
mock(IpConnectivityLog.class));
@@ -1381,39 +1392,75 @@
return null;
}
- void expectAvailableCallbacks(
- MockNetworkAgent agent, boolean expectSuspended, int timeoutMs) {
+ // Expects onAvailable and the callbacks that follow it. These are:
+ // - onSuspended, iff the network was suspended when the callbacks fire.
+ // - onCapabilitiesChanged.
+ // - onLinkPropertiesChanged.
+ //
+ // @param agent the network to expect the callbacks on.
+ // @param expectSuspended whether to expect a SUSPENDED callback.
+ // @param expectValidated the expected value of the VALIDATED capability in the
+ // onCapabilitiesChanged callback.
+ // @param timeoutMs how long to wait for the callbacks.
+ void expectAvailableCallbacks(MockNetworkAgent agent, boolean expectSuspended,
+ boolean expectValidated, int timeoutMs) {
expectCallback(CallbackState.AVAILABLE, agent, timeoutMs);
if (expectSuspended) {
expectCallback(CallbackState.SUSPENDED, agent, timeoutMs);
}
- expectCallback(CallbackState.NETWORK_CAPABILITIES, agent, timeoutMs);
+ if (expectValidated) {
+ expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
+ } else {
+ expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, agent);
+ }
expectCallback(CallbackState.LINK_PROPERTIES, agent, timeoutMs);
}
- void expectAvailableCallbacks(MockNetworkAgent agent) {
- expectAvailableCallbacks(agent, false, TIMEOUT_MS);
+ // Expects the available callbacks (validated), plus onSuspended.
+ void expectAvailableAndSuspendedCallbacks(MockNetworkAgent agent, boolean expectValidated) {
+ expectAvailableCallbacks(agent, true, expectValidated, TIMEOUT_MS);
}
- void expectAvailableAndSuspendedCallbacks(MockNetworkAgent agent) {
- expectAvailableCallbacks(agent, true, TIMEOUT_MS);
+ void expectAvailableCallbacksValidated(MockNetworkAgent agent) {
+ expectAvailableCallbacks(agent, false, true, TIMEOUT_MS);
}
- void expectAvailableAndValidatedCallbacks(MockNetworkAgent agent) {
- expectAvailableCallbacks(agent, false, TIMEOUT_MS);
+ void expectAvailableCallbacksUnvalidated(MockNetworkAgent agent) {
+ expectAvailableCallbacks(agent, false, false, TIMEOUT_MS);
+ }
+
+ // Expects the available callbacks (where the onCapabilitiesChanged must contain the
+ // VALIDATED capability), plus another onCapabilitiesChanged which is identical to the
+ // one we just sent.
+ // TODO: this is likely a bug. Fix it and remove this method.
+ void expectAvailableDoubleValidatedCallbacks(MockNetworkAgent agent) {
+ expectCallback(CallbackState.AVAILABLE, agent, TIMEOUT_MS);
+ NetworkCapabilities nc1 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
+ expectCallback(CallbackState.LINK_PROPERTIES, agent, TIMEOUT_MS);
+ NetworkCapabilities nc2 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
+ assertEquals(nc1, nc2);
+ }
+
+ // Expects the available callbacks where the onCapabilitiesChanged must not have validated,
+ // then expects another onCapabilitiesChanged that has the validated bit set. This is used
+ // when a network connects and satisfies a callback, and then immediately validates.
+ void expectAvailableThenValidatedCallbacks(MockNetworkAgent agent) {
+ expectAvailableCallbacksUnvalidated(agent);
expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
}
- void expectCapabilitiesWith(int capability, MockNetworkAgent agent) {
+ NetworkCapabilities expectCapabilitiesWith(int capability, MockNetworkAgent agent) {
CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent);
NetworkCapabilities nc = (NetworkCapabilities) cbi.arg;
assertTrue(nc.hasCapability(capability));
+ return nc;
}
- void expectCapabilitiesWithout(int capability, MockNetworkAgent agent) {
+ NetworkCapabilities expectCapabilitiesWithout(int capability, MockNetworkAgent agent) {
CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent);
NetworkCapabilities nc = (NetworkCapabilities) cbi.arg;
assertFalse(nc.hasCapability(capability));
+ return nc;
}
void assertNoCallback() {
@@ -1450,8 +1497,8 @@
ConditionVariable cv = waitForConnectivityBroadcasts(1);
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false);
- genericNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent);
- cellNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ genericNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
waitFor(cv);
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1465,8 +1512,8 @@
cv = waitForConnectivityBroadcasts(2);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- genericNetworkCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
- wifiNetworkCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
waitFor(cv);
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1489,8 +1536,8 @@
// Test validated networks
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- genericNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
- cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ genericNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1502,10 +1549,10 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- genericNetworkCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
genericNetworkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
genericNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
- wifiNetworkCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ wifiNetworkCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
cellNetworkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1541,32 +1588,32 @@
mEthernetNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mCellNetworkAgent.connect(true);
- callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.connect(true);
// We get AVAILABLE on wifi when wifi connects and satisfies our unmetered request.
// We then get LOSING when wifi validates and cell is outscored.
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// TODO: Investigate sending validated before losing.
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mEthernetNetworkAgent.connect(true);
- callback.expectAvailableCallbacks(mEthernetNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
// TODO: Investigate sending validated before losing.
callback.expectCallback(CallbackState.LOSING, mWiFiNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mEthernetNetworkAgent);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mEthernetNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
for (int i = 0; i < 4; i++) {
MockNetworkAgent oldNetwork, newNetwork;
@@ -1583,7 +1630,7 @@
callback.expectCallback(CallbackState.LOSING, oldNetwork);
// TODO: should we send an AVAILABLE callback to newNetwork, to indicate that it is no
// longer lingering?
- defaultCallback.expectAvailableCallbacks(newNetwork);
+ defaultCallback.expectAvailableCallbacksValidated(newNetwork);
assertEquals(newNetwork.getNetwork(), mCm.getActiveNetwork());
}
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -1603,7 +1650,7 @@
// Disconnect our test networks.
mWiFiNetworkAgent.disconnect();
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
mCellNetworkAgent.disconnect();
defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
@@ -1619,22 +1666,22 @@
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false); // Score: 10
- callback.expectAvailableCallbacks(mCellNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Bring up wifi with a score of 20.
// Cell stays up because it would satisfy the default request if it validated.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false); // Score: 20
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Bring up wifi with a score of 70.
@@ -1642,33 +1689,33 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.adjustScore(50);
mWiFiNetworkAgent.connect(false); // Score: 70
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Tear down wifi.
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Bring up wifi, then validate it. Previous versions would immediately tear down cell, but
// it's arguably correct to linger it, since it was the default network before it validated.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// TODO: Investigate sending validated before losing.
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
mCellNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
@@ -1676,12 +1723,12 @@
// If a network is lingering, and we add and remove a request from it, resume lingering.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// TODO: Investigate sending validated before losing.
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
@@ -1700,7 +1747,7 @@
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
// Cell is now the default network. Pin it with a cell-specific request.
noopCallback = new NetworkCallback(); // Can't reuse NetworkCallbacks. http://b/20701525
@@ -1709,8 +1756,8 @@
// Now connect wifi, and expect it to become the default network.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- callback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
// The default request is lingering on cell, but nothing happens to cell, and we send no
// callbacks for it, because it's kept up by cellRequest.
callback.assertNoCallback();
@@ -1726,14 +1773,14 @@
// Register a TRACK_DEFAULT request and check that it does not affect lingering.
TestNetworkCallback trackDefaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(trackDefaultCallback);
- trackDefaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ trackDefaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
mEthernetNetworkAgent.connect(true);
- callback.expectAvailableCallbacks(mEthernetNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
callback.expectCallback(CallbackState.LOSING, mWiFiNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
- trackDefaultCallback.expectAvailableAndValidatedCallbacks(mEthernetNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mEthernetNetworkAgent);
+ trackDefaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
// Let linger run its course.
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent, lingerTimeoutMs);
@@ -1760,13 +1807,13 @@
// Bring up validated cell.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
// Bring up unvalidated wifi with explicitlySelected=true.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.explicitlySelected(false);
mWiFiNetworkAgent.connect(false);
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// Cell Remains the default.
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -1789,7 +1836,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.explicitlySelected(false);
mWiFiNetworkAgent.connect(false);
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// If the user chooses no on the "No Internet access, stay connected?" dialog, we ask the
// network to disconnect.
@@ -1800,7 +1847,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.explicitlySelected(false);
mWiFiNetworkAgent.connect(true);
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -1809,7 +1856,7 @@
// TODO: fix this.
mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
mEthernetNetworkAgent.connect(true);
- callback.expectAvailableAndValidatedCallbacks(mEthernetNetworkAgent);
+ callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
callback.assertNoCallback();
@@ -1982,7 +2029,7 @@
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS);
mCellNetworkAgent.connectWithoutInternet();
- networkCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
verifyActiveNetwork(TRANSPORT_WIFI);
// Test releasing NetworkRequest disconnects cellular with MMS
@@ -2011,7 +2058,7 @@
MockNetworkAgent mmsNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mmsNetworkAgent.addCapability(NET_CAPABILITY_MMS);
mmsNetworkAgent.connectWithoutInternet();
- networkCallback.expectAvailableCallbacks(mmsNetworkAgent);
+ networkCallback.expectAvailableCallbacksUnvalidated(mmsNetworkAgent);
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test releasing MMS NetworkRequest does not disconnect main cellular NetworkAgent
@@ -2038,7 +2085,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
String firstRedirectUrl = "http://example.com/firstPath";
mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
- captivePortalCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), firstRedirectUrl);
// Take down network.
@@ -2051,7 +2098,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
String secondRedirectUrl = "http://example.com/secondPath";
mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
- captivePortalCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), secondRedirectUrl);
// Make captive portal disappear then revalidate.
@@ -2061,9 +2108,7 @@
captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
// Expect NET_CAPABILITY_VALIDATED onAvailable callback.
- validatedCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
- // TODO: Investigate only sending available callbacks.
- validatedCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
// Break network connectivity.
// Expect NET_CAPABILITY_VALIDATED onLost callback.
@@ -2087,7 +2132,7 @@
// Bring up wifi.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- validatedCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
// Check that calling startCaptivePortalApp does nothing.
@@ -2098,7 +2143,7 @@
// Turn into a captive portal.
mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 302;
mCm.reportNetworkConnectivity(wifiNetwork, false);
- captivePortalCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
validatedCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
// Check that startCaptivePortalApp sends the expected intent.
@@ -2111,7 +2156,7 @@
mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 204;
CaptivePortal c = (CaptivePortal) intent.getExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL);
c.reportCaptivePortalDismissed();
- validatedCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
mCm.unregisterNetworkCallback(validatedCallback);
@@ -2154,7 +2199,7 @@
mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
// Expect NET_CAPABILITY_VALIDATED onAvailable callback.
- validatedCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
// But there should be no CaptivePortal callback.
captivePortalCallback.assertNoCallback();
}
@@ -2192,14 +2237,14 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- cEmpty1.expectAvailableCallbacks(mWiFiNetworkAgent);
- cEmpty2.expectAvailableCallbacks(mWiFiNetworkAgent);
- cEmpty3.expectAvailableCallbacks(mWiFiNetworkAgent);
- cEmpty4.expectAvailableCallbacks(mWiFiNetworkAgent);
+ cEmpty1.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ cEmpty2.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ cEmpty3.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ cEmpty4.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertNoCallbacks(cFoo, cBar);
mWiFiNetworkAgent.setNetworkSpecifier(new StringNetworkSpecifier("foo"));
- cFoo.expectAvailableCallbacks(mWiFiNetworkAgent);
+ cFoo.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
for (TestNetworkCallback c: emptyCallbacks) {
c.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
}
@@ -2208,7 +2253,7 @@
mWiFiNetworkAgent.setNetworkSpecifier(new StringNetworkSpecifier("bar"));
cFoo.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- cBar.expectAvailableCallbacks(mWiFiNetworkAgent);
+ cBar.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
for (TestNetworkCallback c: emptyCallbacks) {
c.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
}
@@ -2337,14 +2382,14 @@
// Bring up cell and expect CALLBACK_AVAILABLE.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
- defaultNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
// Bring up wifi and expect CALLBACK_AVAILABLE.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
cellNetworkCallback.assertNoCallback();
- defaultNetworkCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ defaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
// Bring down cell. Expect no default network callback, since it wasn't the default.
mCellNetworkAgent.disconnect();
@@ -2354,7 +2399,7 @@
// Bring up cell. Expect no default network callback, since it won't be the default.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
defaultNetworkCallback.assertNoCallback();
// Bring down wifi. Expect the default network callback to notified of LOST wifi
@@ -2362,7 +2407,7 @@
mWiFiNetworkAgent.disconnect();
cellNetworkCallback.assertNoCallback();
defaultNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
mCellNetworkAgent.disconnect();
cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
defaultNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
@@ -2383,7 +2428,7 @@
// We should get onAvailable(), onCapabilitiesChanged(), and
// onLinkPropertiesChanged() in rapid succession. Additionally, we
// should get onCapabilitiesChanged() when the mobile network validates.
- cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
// Update LinkProperties.
@@ -2404,7 +2449,7 @@
mCm.registerDefaultNetworkCallback(dfltNetworkCallback);
// We should get onAvailable(), onCapabilitiesChanged(), onLinkPropertiesChanged(),
// as well as onNetworkSuspended() in rapid succession.
- dfltNetworkCallback.expectAvailableAndSuspendedCallbacks(mCellNetworkAgent);
+ dfltNetworkCallback.expectAvailableAndSuspendedCallbacks(mCellNetworkAgent, true);
dfltNetworkCallback.assertNoCallback();
mCm.unregisterNetworkCallback(dfltNetworkCallback);
@@ -2444,18 +2489,18 @@
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
- fgCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ fgCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertTrue(isForegroundNetwork(mCellNetworkAgent));
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
// When wifi connects, cell lingers.
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
- fgCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ fgCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
fgCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
fgCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
assertTrue(isForegroundNetwork(mCellNetworkAgent));
@@ -2479,8 +2524,8 @@
// is currently delivered before the onAvailable() callbacks.
// TODO: Fix this.
cellCallback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
- cellCallback.expectAvailableCallbacks(mCellNetworkAgent);
- fgCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ cellCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ fgCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
// Expect a network capabilities update with FOREGROUND, because the most recent
// request causes its state to change.
callback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
@@ -2500,7 +2545,7 @@
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
fgCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- fgCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ fgCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertTrue(isForegroundNetwork(mCellNetworkAgent));
mCm.unregisterNetworkCallback(callback);
@@ -2640,7 +2685,7 @@
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
testFactory.expectAddRequests(2); // Because the cell request changes score twice.
mCellNetworkAgent.connect(true);
- cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
testFactory.waitForNetworkRequests(2);
assertFalse(testFactory.getMyStartRequested()); // Because the cell network outscores us.
@@ -2731,16 +2776,15 @@
// Bring up validated cell.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
Network cellNetwork = mCellNetworkAgent.getNetwork();
// Bring up validated wifi.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
- validatedWifiCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
- validatedWifiCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ validatedWifiCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
// Fail validation on wifi.
@@ -2761,18 +2805,18 @@
// that we switch back to cell.
tracker.configRestrictsAvoidBadWifi = false;
tracker.reevaluate();
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertEquals(mCm.getActiveNetwork(), cellNetwork);
// Switch back to a restrictive carrier.
tracker.configRestrictsAvoidBadWifi = true;
tracker.reevaluate();
- defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mCm.getActiveNetwork(), wifiNetwork);
// Simulate the user selecting "switch" on the dialog, and check that we switch to cell.
mCm.setAvoidUnvalidated(wifiNetwork);
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability(
NET_CAPABILITY_VALIDATED));
assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability(
@@ -2783,9 +2827,8 @@
mWiFiNetworkAgent.disconnect();
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
- validatedWifiCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
- validatedWifiCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ validatedWifiCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
wifiNetwork = mWiFiNetworkAgent.getNetwork();
// Fail validation on wifi and expect the dialog to appear.
@@ -2799,7 +2842,7 @@
tracker.reevaluate();
// We now switch to cell.
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability(
NET_CAPABILITY_VALIDATED));
assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability(
@@ -2810,17 +2853,17 @@
// We switch to wifi and then to cell.
Settings.Global.putString(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
tracker.reevaluate();
- defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mCm.getActiveNetwork(), wifiNetwork);
Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1);
tracker.reevaluate();
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertEquals(mCm.getActiveNetwork(), cellNetwork);
// If cell goes down, we switch to wifi.
mCellNetworkAgent.disconnect();
defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
validatedWifiCallback.assertNoCallback();
mCm.unregisterNetworkCallback(cellNetworkCallback);
@@ -2862,7 +2905,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, timeoutMs);
+ networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, timeoutMs);
// pass timeout and validate that UNAVAILABLE is not called
networkCallback.assertNoCallback();
@@ -2883,7 +2926,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
final int assertTimeoutMs = 100;
- networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, assertTimeoutMs);
+ networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, assertTimeoutMs);
mWiFiNetworkAgent.disconnect();
networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
@@ -3370,7 +3413,7 @@
// Bring up wifi aware network.
wifiAware.connect(false, false);
- callback.expectAvailableCallbacks(wifiAware);
+ callback.expectAvailableCallbacksUnvalidated(wifiAware);
assertNull(mCm.getActiveNetworkInfo());
assertNull(mCm.getActiveNetwork());
@@ -3489,6 +3532,44 @@
reset(mStatsService);
}
+ @Test
+ public void testBasicDnsConfigurationPushed() throws Exception {
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ waitForIdle();
+ verify(mNetworkManagementService, never()).setDnsConfigurationForNetwork(
+ anyInt(), any(), any(), any(), anyBoolean(), anyString());
+
+ final LinkProperties cellLp = new LinkProperties();
+ cellLp.setInterfaceName("test_rmnet_data0");
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ mCellNetworkAgent.connect(false);
+ waitForIdle();
+ verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
+ anyInt(), mStringArrayCaptor.capture(), any(), any(), anyBoolean(), anyString());
+ // CS tells netd about the empty DNS config for this network.
+ assertEmpty(mStringArrayCaptor.getValue());
+ reset(mNetworkManagementService);
+
+ cellLp.addDnsServer(InetAddress.getByName("2001:db8::1"));
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ waitForIdle();
+ verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
+ anyInt(), mStringArrayCaptor.capture(), any(), any(), anyBoolean(), anyString());
+ assertEquals(1, mStringArrayCaptor.getValue().length);
+ assertTrue(ArrayUtils.contains(mStringArrayCaptor.getValue(), "2001:db8::1"));
+ reset(mNetworkManagementService);
+
+ cellLp.addDnsServer(InetAddress.getByName("192.0.2.1"));
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ waitForIdle();
+ verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
+ anyInt(), mStringArrayCaptor.capture(), any(), any(), anyBoolean(), anyString());
+ assertEquals(2, mStringArrayCaptor.getValue().length);
+ assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
+ new String[]{"2001:db8::1", "192.0.2.1"}));
+ reset(mNetworkManagementService);
+ }
+
private void checkDirectlyConnectedRoutes(Object callbackObj,
Collection<LinkAddress> linkAddresses, Collection<RouteInfo> otherRoutes) {
assertTrue(callbackObj instanceof LinkProperties);
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 2282c13..4fbb228 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -32,7 +32,6 @@
import android.net.IpSecConfig;
import android.net.IpSecManager;
import android.net.IpSecSpiResponse;
-import android.net.IpSecTransform;
import android.net.IpSecTransformResponse;
import android.net.NetworkUtils;
import android.os.Binder;
@@ -54,14 +53,14 @@
@RunWith(Parameterized.class)
public class IpSecServiceParameterizedTest {
- private static final int TEST_SPI_OUT = 0xD1201D;
- private static final int TEST_SPI_IN = TEST_SPI_OUT + 1;
+ private static final int TEST_SPI = 0xD1201D;
- private final String mRemoteAddr;
+ private final String mDestinationAddr;
+ private final String mSourceAddr;
@Parameterized.Parameters
public static Collection ipSecConfigs() {
- return Arrays.asList(new Object[][] {{"8.8.4.4"}, {"2601::10"}});
+ return Arrays.asList(new Object[][] {{"1.2.3.4", "8.8.4.4"}, {"2601::2", "2601::10"}});
}
private static final byte[] AEAD_KEY = {
@@ -96,11 +95,9 @@
private static final IpSecAlgorithm AEAD_ALGO =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
- private static final int[] DIRECTIONS =
- new int[] {IpSecTransform.DIRECTION_IN, IpSecTransform.DIRECTION_OUT};
-
- public IpSecServiceParameterizedTest(String remoteAddr) {
- mRemoteAddr = remoteAddr;
+ public IpSecServiceParameterizedTest(String sourceAddr, String destAddr) {
+ mSourceAddr = sourceAddr;
+ mDestinationAddr = destAddr;
}
@Before
@@ -116,44 +113,30 @@
@Test
public void testIpSecServiceReserveSpi() throws Exception {
- when(mMockNetd.ipSecAllocateSpi(
- anyInt(),
- eq(IpSecTransform.DIRECTION_OUT),
- anyString(),
- eq(mRemoteAddr),
- eq(TEST_SPI_OUT)))
- .thenReturn(TEST_SPI_OUT);
+ when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI)))
+ .thenReturn(TEST_SPI);
IpSecSpiResponse spiResp =
mIpSecService.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder());
+ mDestinationAddr, TEST_SPI, new Binder());
assertEquals(IpSecManager.Status.OK, spiResp.status);
- assertEquals(TEST_SPI_OUT, spiResp.spi);
+ assertEquals(TEST_SPI, spiResp.spi);
}
@Test
public void testReleaseSecurityParameterIndex() throws Exception {
- when(mMockNetd.ipSecAllocateSpi(
- anyInt(),
- eq(IpSecTransform.DIRECTION_OUT),
- anyString(),
- eq(mRemoteAddr),
- eq(TEST_SPI_OUT)))
- .thenReturn(TEST_SPI_OUT);
+ when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI)))
+ .thenReturn(TEST_SPI);
IpSecSpiResponse spiResp =
mIpSecService.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder());
+ mDestinationAddr, TEST_SPI, new Binder());
mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
- eq(spiResp.resourceId),
- anyInt(),
- anyString(),
- anyString(),
- eq(TEST_SPI_OUT));
+ eq(spiResp.resourceId), anyString(), anyString(), eq(TEST_SPI));
// Verify quota and RefcountedResource objects cleaned up
IpSecService.UserRecord userRecord =
@@ -169,17 +152,12 @@
@Test
public void testSecurityParameterIndexBinderDeath() throws Exception {
- when(mMockNetd.ipSecAllocateSpi(
- anyInt(),
- eq(IpSecTransform.DIRECTION_OUT),
- anyString(),
- eq(mRemoteAddr),
- eq(TEST_SPI_OUT)))
- .thenReturn(TEST_SPI_OUT);
+ when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI)))
+ .thenReturn(TEST_SPI);
IpSecSpiResponse spiResp =
mIpSecService.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder());
+ mDestinationAddr, TEST_SPI, new Binder());
IpSecService.UserRecord userRecord =
mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
@@ -190,11 +168,7 @@
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
- eq(spiResp.resourceId),
- anyInt(),
- anyString(),
- anyString(),
- eq(TEST_SPI_OUT));
+ eq(spiResp.resourceId), anyString(), anyString(), eq(TEST_SPI));
// Verify quota and RefcountedResource objects cleaned up
assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent);
@@ -206,14 +180,12 @@
}
}
- private int getNewSpiResourceId(int direction, String remoteAddress, int returnSpi)
- throws Exception {
- when(mMockNetd.ipSecAllocateSpi(anyInt(), anyInt(), anyString(), anyString(), anyInt()))
+ private int getNewSpiResourceId(String remoteAddress, int returnSpi) throws Exception {
+ when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), anyString(), anyInt()))
.thenReturn(returnSpi);
IpSecSpiResponse spi =
mIpSecService.allocateSecurityParameterIndex(
- direction,
NetworkUtils.numericToInetAddress(remoteAddress).getHostAddress(),
IpSecManager.INVALID_SECURITY_PARAMETER_INDEX,
new Binder());
@@ -221,20 +193,14 @@
}
private void addDefaultSpisAndRemoteAddrToIpSecConfig(IpSecConfig config) throws Exception {
- config.setSpiResourceId(
- IpSecTransform.DIRECTION_OUT,
- getNewSpiResourceId(IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT));
- config.setSpiResourceId(
- IpSecTransform.DIRECTION_IN,
- getNewSpiResourceId(IpSecTransform.DIRECTION_IN, mRemoteAddr, TEST_SPI_IN));
- config.setRemoteAddress(mRemoteAddr);
+ config.setSpiResourceId(getNewSpiResourceId(mDestinationAddr, TEST_SPI));
+ config.setSourceAddress(mSourceAddr);
+ config.setDestinationAddress(mDestinationAddr);
}
private void addAuthAndCryptToIpSecConfig(IpSecConfig config) throws Exception {
- for (int direction : DIRECTIONS) {
- config.setEncryption(direction, CRYPT_ALGO);
- config.setAuthentication(direction, AUTH_ALGO);
- }
+ config.setEncryption(CRYPT_ALGO);
+ config.setAuthentication(AUTH_ALGO);
}
@Test
@@ -251,32 +217,10 @@
.ipSecAddSecurityAssociation(
eq(createTransformResp.resourceId),
anyInt(),
- eq(IpSecTransform.DIRECTION_OUT),
anyString(),
anyString(),
anyLong(),
- eq(TEST_SPI_OUT),
- eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
- eq(AUTH_KEY),
- anyInt(),
- eq(IpSecAlgorithm.CRYPT_AES_CBC),
- eq(CRYPT_KEY),
- anyInt(),
- eq(""),
- eq(new byte[] {}),
- eq(0),
- anyInt(),
- anyInt(),
- anyInt());
- verify(mMockNetd)
- .ipSecAddSecurityAssociation(
- eq(createTransformResp.resourceId),
- anyInt(),
- eq(IpSecTransform.DIRECTION_IN),
- anyString(),
- anyString(),
- anyLong(),
- eq(TEST_SPI_IN),
+ eq(TEST_SPI),
eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
eq(AUTH_KEY),
anyInt(),
@@ -296,8 +240,7 @@
IpSecConfig ipSecConfig = new IpSecConfig();
addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
- ipSecConfig.setAuthenticatedEncryption(IpSecTransform.DIRECTION_OUT, AEAD_ALGO);
- ipSecConfig.setAuthenticatedEncryption(IpSecTransform.DIRECTION_IN, AEAD_ALGO);
+ ipSecConfig.setAuthenticatedEncryption(AEAD_ALGO);
IpSecTransformResponse createTransformResp =
mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
@@ -307,32 +250,10 @@
.ipSecAddSecurityAssociation(
eq(createTransformResp.resourceId),
anyInt(),
- eq(IpSecTransform.DIRECTION_OUT),
anyString(),
anyString(),
anyLong(),
- eq(TEST_SPI_OUT),
- eq(""),
- eq(new byte[] {}),
- eq(0),
- eq(""),
- eq(new byte[] {}),
- eq(0),
- eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
- eq(AEAD_KEY),
- anyInt(),
- anyInt(),
- anyInt(),
- anyInt());
- verify(mMockNetd)
- .ipSecAddSecurityAssociation(
- eq(createTransformResp.resourceId),
- anyInt(),
- eq(IpSecTransform.DIRECTION_IN),
- anyString(),
- anyString(),
- anyLong(),
- eq(TEST_SPI_IN),
+ eq(TEST_SPI),
eq(""),
eq(new byte[] {}),
eq(0),
@@ -359,18 +280,7 @@
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
- eq(createTransformResp.resourceId),
- eq(IpSecTransform.DIRECTION_OUT),
- anyString(),
- anyString(),
- eq(TEST_SPI_OUT));
- verify(mMockNetd)
- .ipSecDeleteSecurityAssociation(
- eq(createTransformResp.resourceId),
- eq(IpSecTransform.DIRECTION_IN),
- anyString(),
- anyString(),
- eq(TEST_SPI_IN));
+ eq(createTransformResp.resourceId), anyString(), anyString(), eq(TEST_SPI));
// Verify quota and RefcountedResource objects cleaned up
IpSecService.UserRecord userRecord =
@@ -404,18 +314,7 @@
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
- eq(createTransformResp.resourceId),
- eq(IpSecTransform.DIRECTION_OUT),
- anyString(),
- anyString(),
- eq(TEST_SPI_OUT));
- verify(mMockNetd)
- .ipSecDeleteSecurityAssociation(
- eq(createTransformResp.resourceId),
- eq(IpSecTransform.DIRECTION_IN),
- anyString(),
- anyString(),
- eq(TEST_SPI_IN));
+ eq(createTransformResp.resourceId), anyString(), anyString(), eq(TEST_SPI));
// Verify quota and RefcountedResource objects cleaned up
assertEquals(0, userRecord.mTransformQuotaTracker.mCurrent);
@@ -439,30 +338,22 @@
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
int resourceId = createTransformResp.resourceId;
- mIpSecService.applyTransportModeTransform(pfd, resourceId);
+ mIpSecService.applyTransportModeTransform(pfd, IpSecManager.DIRECTION_OUT, resourceId);
verify(mMockNetd)
.ipSecApplyTransportModeTransform(
eq(pfd.getFileDescriptor()),
eq(resourceId),
- eq(IpSecTransform.DIRECTION_OUT),
+ eq(IpSecManager.DIRECTION_OUT),
anyString(),
anyString(),
- eq(TEST_SPI_OUT));
- verify(mMockNetd)
- .ipSecApplyTransportModeTransform(
- eq(pfd.getFileDescriptor()),
- eq(resourceId),
- eq(IpSecTransform.DIRECTION_IN),
- anyString(),
- anyString(),
- eq(TEST_SPI_IN));
+ eq(TEST_SPI));
}
@Test
public void testRemoveTransportModeTransform() throws Exception {
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
- mIpSecService.removeTransportModeTransform(pfd, 1);
+ mIpSecService.removeTransportModeTransforms(pfd);
verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
}
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 0467989..3eba881 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -105,9 +105,6 @@
private static final IpSecAlgorithm AEAD_ALGO =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
- private static final int[] DIRECTIONS =
- new int[] {IpSecTransform.DIRECTION_IN, IpSecTransform.DIRECTION_OUT};
-
static {
try {
INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
@@ -303,83 +300,75 @@
@Test
public void testValidateAlgorithmsAuth() {
- for (int direction : DIRECTIONS) {
- // Validate that correct algorithm type succeeds
- IpSecConfig config = new IpSecConfig();
- config.setAuthentication(direction, AUTH_ALGO);
- mIpSecService.validateAlgorithms(config, direction);
+ // Validate that correct algorithm type succeeds
+ IpSecConfig config = new IpSecConfig();
+ config.setAuthentication(AUTH_ALGO);
+ mIpSecService.validateAlgorithms(config);
- // Validate that incorrect algorithm types fails
- for (IpSecAlgorithm algo : new IpSecAlgorithm[] {CRYPT_ALGO, AEAD_ALGO}) {
- try {
- config = new IpSecConfig();
- config.setAuthentication(direction, algo);
- mIpSecService.validateAlgorithms(config, direction);
- fail("Did not throw exception on invalid algorithm type");
- } catch (IllegalArgumentException expected) {
- }
+ // Validate that incorrect algorithm types fails
+ for (IpSecAlgorithm algo : new IpSecAlgorithm[] {CRYPT_ALGO, AEAD_ALGO}) {
+ try {
+ config = new IpSecConfig();
+ config.setAuthentication(algo);
+ mIpSecService.validateAlgorithms(config);
+ fail("Did not throw exception on invalid algorithm type");
+ } catch (IllegalArgumentException expected) {
}
}
}
@Test
public void testValidateAlgorithmsCrypt() {
- for (int direction : DIRECTIONS) {
- // Validate that correct algorithm type succeeds
- IpSecConfig config = new IpSecConfig();
- config.setEncryption(direction, CRYPT_ALGO);
- mIpSecService.validateAlgorithms(config, direction);
+ // Validate that correct algorithm type succeeds
+ IpSecConfig config = new IpSecConfig();
+ config.setEncryption(CRYPT_ALGO);
+ mIpSecService.validateAlgorithms(config);
- // Validate that incorrect algorithm types fails
- for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, AEAD_ALGO}) {
- try {
- config = new IpSecConfig();
- config.setEncryption(direction, algo);
- mIpSecService.validateAlgorithms(config, direction);
- fail("Did not throw exception on invalid algorithm type");
- } catch (IllegalArgumentException expected) {
- }
+ // Validate that incorrect algorithm types fails
+ for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, AEAD_ALGO}) {
+ try {
+ config = new IpSecConfig();
+ config.setEncryption(algo);
+ mIpSecService.validateAlgorithms(config);
+ fail("Did not throw exception on invalid algorithm type");
+ } catch (IllegalArgumentException expected) {
}
}
}
@Test
public void testValidateAlgorithmsAead() {
- for (int direction : DIRECTIONS) {
- // Validate that correct algorithm type succeeds
- IpSecConfig config = new IpSecConfig();
- config.setAuthenticatedEncryption(direction, AEAD_ALGO);
- mIpSecService.validateAlgorithms(config, direction);
+ // Validate that correct algorithm type succeeds
+ IpSecConfig config = new IpSecConfig();
+ config.setAuthenticatedEncryption(AEAD_ALGO);
+ mIpSecService.validateAlgorithms(config);
- // Validate that incorrect algorithm types fails
- for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, CRYPT_ALGO}) {
- try {
- config = new IpSecConfig();
- config.setAuthenticatedEncryption(direction, algo);
- mIpSecService.validateAlgorithms(config, direction);
- fail("Did not throw exception on invalid algorithm type");
- } catch (IllegalArgumentException expected) {
- }
+ // Validate that incorrect algorithm types fails
+ for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, CRYPT_ALGO}) {
+ try {
+ config = new IpSecConfig();
+ config.setAuthenticatedEncryption(algo);
+ mIpSecService.validateAlgorithms(config);
+ fail("Did not throw exception on invalid algorithm type");
+ } catch (IllegalArgumentException expected) {
}
}
}
@Test
public void testValidateAlgorithmsAuthCrypt() {
- for (int direction : DIRECTIONS) {
- // Validate that correct algorithm type succeeds
- IpSecConfig config = new IpSecConfig();
- config.setAuthentication(direction, AUTH_ALGO);
- config.setEncryption(direction, CRYPT_ALGO);
- mIpSecService.validateAlgorithms(config, direction);
- }
+ // Validate that correct algorithm type succeeds
+ IpSecConfig config = new IpSecConfig();
+ config.setAuthentication(AUTH_ALGO);
+ config.setEncryption(CRYPT_ALGO);
+ mIpSecService.validateAlgorithms(config);
}
@Test
public void testValidateAlgorithmsNoAlgorithms() {
IpSecConfig config = new IpSecConfig();
try {
- mIpSecService.validateAlgorithms(config, IpSecTransform.DIRECTION_IN);
+ mIpSecService.validateAlgorithms(config);
fail("Expected exception; no algorithms specified");
} catch (IllegalArgumentException expected) {
}
@@ -388,10 +377,10 @@
@Test
public void testValidateAlgorithmsAeadWithAuth() {
IpSecConfig config = new IpSecConfig();
- config.setAuthenticatedEncryption(IpSecTransform.DIRECTION_IN, AEAD_ALGO);
- config.setAuthentication(IpSecTransform.DIRECTION_IN, AUTH_ALGO);
+ config.setAuthenticatedEncryption(AEAD_ALGO);
+ config.setAuthentication(AUTH_ALGO);
try {
- mIpSecService.validateAlgorithms(config, IpSecTransform.DIRECTION_IN);
+ mIpSecService.validateAlgorithms(config);
fail("Expected exception; both AEAD and auth algorithm specified");
} catch (IllegalArgumentException expected) {
}
@@ -400,10 +389,10 @@
@Test
public void testValidateAlgorithmsAeadWithCrypt() {
IpSecConfig config = new IpSecConfig();
- config.setAuthenticatedEncryption(IpSecTransform.DIRECTION_IN, AEAD_ALGO);
- config.setEncryption(IpSecTransform.DIRECTION_IN, CRYPT_ALGO);
+ config.setAuthenticatedEncryption(AEAD_ALGO);
+ config.setEncryption(CRYPT_ALGO);
try {
- mIpSecService.validateAlgorithms(config, IpSecTransform.DIRECTION_IN);
+ mIpSecService.validateAlgorithms(config);
fail("Expected exception; both AEAD and crypt algorithm specified");
} catch (IllegalArgumentException expected) {
}
@@ -412,11 +401,11 @@
@Test
public void testValidateAlgorithmsAeadWithAuthAndCrypt() {
IpSecConfig config = new IpSecConfig();
- config.setAuthenticatedEncryption(IpSecTransform.DIRECTION_IN, AEAD_ALGO);
- config.setAuthentication(IpSecTransform.DIRECTION_IN, AUTH_ALGO);
- config.setEncryption(IpSecTransform.DIRECTION_IN, CRYPT_ALGO);
+ config.setAuthenticatedEncryption(AEAD_ALGO);
+ config.setAuthentication(AUTH_ALGO);
+ config.setEncryption(CRYPT_ALGO);
try {
- mIpSecService.validateAlgorithms(config, IpSecTransform.DIRECTION_IN);
+ mIpSecService.validateAlgorithms(config);
fail("Expected exception; AEAD, auth and crypt algorithm specified");
} catch (IllegalArgumentException expected) {
}
@@ -434,7 +423,7 @@
@Test
public void testRemoveTransportModeTransform() throws Exception {
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
- mIpSecService.removeTransportModeTransform(pfd, 1);
+ mIpSecService.removeTransportModeTransforms(pfd);
verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
}
@@ -447,7 +436,7 @@
try {
IpSecSpiResponse spiResp =
mIpSecService.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, address, DROID_SPI, new Binder());
+ address, DROID_SPI, new Binder());
fail("Invalid address was passed through IpSecService validation: " + address);
} catch (IllegalArgumentException e) {
} catch (Exception e) {
@@ -519,7 +508,6 @@
// tracks the resource ID.
when(mMockNetd.ipSecAllocateSpi(
anyInt(),
- eq(IpSecTransform.DIRECTION_OUT),
anyString(),
eq(InetAddress.getLoopbackAddress().getHostAddress()),
anyInt()))
@@ -528,7 +516,6 @@
for (int i = 0; i < MAX_NUM_SPIS; i++) {
IpSecSpiResponse newSpi =
mIpSecService.allocateSecurityParameterIndex(
- 0x1,
InetAddress.getLoopbackAddress().getHostAddress(),
DROID_SPI + i,
new Binder());
@@ -544,7 +531,6 @@
// Try to reserve one more SPI, and should fail.
IpSecSpiResponse extraSpi =
mIpSecService.allocateSecurityParameterIndex(
- 0x1,
InetAddress.getLoopbackAddress().getHostAddress(),
DROID_SPI + MAX_NUM_SPIS,
new Binder());
@@ -558,7 +544,6 @@
// Should successfully reserve one more spi.
extraSpi =
mIpSecService.allocateSecurityParameterIndex(
- 0x1,
InetAddress.getLoopbackAddress().getHostAddress(),
DROID_SPI + MAX_NUM_SPIS,
new Binder());
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index c29363c..1dbf9b2 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -22,6 +22,7 @@
import static android.content.pm.UserInfo.FLAG_RESTRICTED;
import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -436,11 +437,13 @@
.addTransportType(TRANSPORT_CELLULAR)
.addCapability(NET_CAPABILITY_INTERNET)
.addCapability(NET_CAPABILITY_NOT_METERED)
+ .addCapability(NET_CAPABILITY_NOT_CONGESTED)
.setLinkDownstreamBandwidthKbps(10));
networks.put(wifi, new NetworkCapabilities()
.addTransportType(TRANSPORT_WIFI)
.addCapability(NET_CAPABILITY_INTERNET)
.addCapability(NET_CAPABILITY_NOT_ROAMING)
+ .addCapability(NET_CAPABILITY_NOT_CONGESTED)
.setLinkUpstreamBandwidthKbps(20));
setMockedNetworks(networks);
@@ -454,6 +457,7 @@
assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps());
assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+ assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
Vpn.updateCapabilities(mConnectivityManager, new Network[] { mobile }, caps);
assertTrue(caps.hasTransport(TRANSPORT_VPN));
@@ -463,6 +467,7 @@
assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps());
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+ assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
Vpn.updateCapabilities(mConnectivityManager, new Network[] { wifi }, caps);
assertTrue(caps.hasTransport(TRANSPORT_VPN));
@@ -472,6 +477,7 @@
assertEquals(20, caps.getLinkUpstreamBandwidthKbps());
assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+ assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
Vpn.updateCapabilities(mConnectivityManager, new Network[] { mobile, wifi }, caps);
assertTrue(caps.hasTransport(TRANSPORT_VPN));
@@ -481,6 +487,7 @@
assertEquals(20, caps.getLinkUpstreamBandwidthKbps());
assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+ assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
}
/**
diff --git a/tests/testables/src/android/testing/PollingCheck.java b/tests/testables/src/android/testing/PollingCheck.java
new file mode 100644
index 0000000..5a31450
--- /dev/null
+++ b/tests/testables/src/android/testing/PollingCheck.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.testing;
+
+import junit.framework.Assert;
+import java.util.concurrent.Callable;
+
+public abstract class PollingCheck {
+ private static final long TIME_SLICE = 50;
+ private long mTimeout = 3000;
+
+ public static interface PollingCheckCondition {
+ boolean canProceed();
+ }
+
+ public PollingCheck() {
+ }
+
+ public PollingCheck(long timeout) {
+ mTimeout = timeout;
+ }
+
+ protected abstract boolean check();
+
+ public void run() {
+ if (check()) {
+ return;
+ }
+
+ long timeout = mTimeout;
+ while (timeout > 0) {
+ try {
+ Thread.sleep(TIME_SLICE);
+ } catch (InterruptedException e) {
+ Assert.fail("unexpected InterruptedException");
+ }
+
+ if (check()) {
+ return;
+ }
+
+ timeout -= TIME_SLICE;
+ }
+
+ Assert.fail("unexpected timeout");
+ }
+
+ public static void check(CharSequence message, long timeout, Callable<Boolean> condition)
+ throws Exception {
+ while (timeout > 0) {
+ if (condition.call()) {
+ return;
+ }
+
+ Thread.sleep(TIME_SLICE);
+ timeout -= TIME_SLICE;
+ }
+
+ Assert.fail(message.toString());
+ }
+
+ public static void waitFor(final PollingCheckCondition condition) {
+ new PollingCheck() {
+ @Override
+ protected boolean check() {
+ return condition.canProceed();
+ }
+ }.run();
+ }
+
+ public static void waitFor(long timeout, final PollingCheckCondition condition) {
+ new PollingCheck(timeout) {
+ @Override
+ protected boolean check() {
+ return condition.canProceed();
+ }
+ }.run();
+ }
+}
+
diff --git a/tools/aapt2/LoadedApk.h b/tools/aapt2/LoadedApk.h
index 6d2257f..81bcecc 100644
--- a/tools/aapt2/LoadedApk.h
+++ b/tools/aapt2/LoadedApk.h
@@ -42,6 +42,8 @@
// Info about an APK loaded in memory.
class LoadedApk {
public:
+ virtual ~LoadedApk() = default;
+
// Loads both binary and proto APKs from disk.
static std::unique_ptr<LoadedApk> LoadApkFromPath(const ::android::StringPiece& path,
IDiagnostics* diag);
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 24b28dd..7cffeea 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -17,6 +17,7 @@
#include "ResourceParser.h"
#include <functional>
+#include <limits>
#include <sstream>
#include "android-base/logging.h"
@@ -987,8 +988,7 @@
type_mask = ParseFormatAttribute(maybe_format.value());
if (type_mask == 0) {
diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
- << "invalid attribute format '" << maybe_format.value()
- << "'");
+ << "invalid attribute format '" << maybe_format.value() << "'");
return false;
}
}
@@ -1000,8 +1000,7 @@
if (!min_str.empty()) {
std::u16string min_str16 = util::Utf8ToUtf16(min_str);
android::Res_value value;
- if (android::ResTable::stringToInt(min_str16.data(), min_str16.size(),
- &value)) {
+ if (android::ResTable::stringToInt(min_str16.data(), min_str16.size(), &value)) {
maybe_min = static_cast<int32_t>(value.data);
}
}
@@ -1018,8 +1017,7 @@
if (!max_str.empty()) {
std::u16string max_str16 = util::Utf8ToUtf16(max_str);
android::Res_value value;
- if (android::ResTable::stringToInt(max_str16.data(), max_str16.size(),
- &value)) {
+ if (android::ResTable::stringToInt(max_str16.data(), max_str16.size(), &value)) {
maybe_max = static_cast<int32_t>(value.data);
}
}
@@ -1061,8 +1059,7 @@
const Source item_source = source_.WithLine(parser->line_number());
const std::string& element_namespace = parser->element_namespace();
const std::string& element_name = parser->element_name();
- if (element_namespace.empty() &&
- (element_name == "flag" || element_name == "enum")) {
+ if (element_namespace.empty() && (element_name == "flag" || element_name == "enum")) {
if (element_name == "enum") {
if (type_mask & android::ResTable_map::TYPE_FLAGS) {
diag_->Error(DiagMessage(item_source)
@@ -1120,17 +1117,12 @@
return false;
}
- std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(weak);
+ std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(
+ type_mask ? type_mask : uint32_t{android::ResTable_map::TYPE_ANY});
+ attr->SetWeak(weak);
attr->symbols = std::vector<Attribute::Symbol>(items.begin(), items.end());
- attr->type_mask =
- type_mask ? type_mask : uint32_t(android::ResTable_map::TYPE_ANY);
- if (maybe_min) {
- attr->min_int = maybe_min.value();
- }
-
- if (maybe_max) {
- attr->max_int = maybe_max.value();
- }
+ attr->min_int = maybe_min.value_or_default(std::numeric_limits<int32_t>::min());
+ attr->max_int = maybe_max.value_or_default(std::numeric_limits<int32_t>::max());
out_resource->value = std::move(attr);
return true;
}
@@ -1445,11 +1437,9 @@
const std::string& element_namespace = parser->element_namespace();
const std::string& element_name = parser->element_name();
if (element_namespace.empty() && element_name == "attr") {
- Maybe<StringPiece> maybe_name =
- xml::FindNonEmptyAttribute(parser, "name");
+ Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
if (!maybe_name) {
- diag_->Error(DiagMessage(item_source)
- << "<attr> tag must have a 'name' attribute");
+ diag_->Error(DiagMessage(item_source) << "<attr> tag must have a 'name' attribute");
error = true;
continue;
}
@@ -1457,8 +1447,7 @@
// If this is a declaration, the package name may be in the name. Separate
// these out.
// Eg. <attr name="android:text" />
- Maybe<Reference> maybe_ref =
- ResourceUtils::ParseXmlAttributeName(maybe_name.value());
+ Maybe<Reference> maybe_ref = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
if (!maybe_ref) {
diag_->Error(DiagMessage(item_source) << "<attr> tag has invalid name '"
<< maybe_name.value() << "'");
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 3172892..9905f82 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -262,9 +262,8 @@
// attributes all-over, we do special handling to see
// which definition sticks.
//
- if (existing_attr->type_mask == incoming_attr->type_mask) {
- // The two attributes are both DECLs, but they are plain attributes
- // with the same formats.
+ if (existing_attr->IsCompatibleWith(*incoming_attr)) {
+ // The two attributes are both DECLs, but they are plain attributes with compatible formats.
// Keep the strongest one.
return existing_attr->IsWeak() ? CollisionResult::kTakeNew : CollisionResult::kKeepOriginal;
}
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index eaa2d0b..95e30c4 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -81,10 +81,7 @@
DISALLOW_COPY_AND_ASSIGN(ResourceConfigValue);
};
-/**
- * Represents a resource entry, which may have
- * varying values for each defined configuration.
- */
+// Represents a resource entry, which may have varying values for each defined configuration.
class ResourceEntry {
public:
// The name of the resource. Immutable, as this determines the order of this resource
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index eb75f94..7fa8ea2 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -102,23 +102,37 @@
TEST(ResourceTableTest, OverrideWeakResourceValue) {
ResourceTable table;
- ASSERT_TRUE(table.AddResource(
- test::ParseNameOrDie("android:attr/foo"), ConfigDescription{}, "",
- util::make_unique<Attribute>(true), test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(test::ParseNameOrDie("android:attr/foo"), ConfigDescription{}, "",
+ test::AttributeBuilder().SetWeak(true).Build(),
+ test::GetDiagnostics()));
Attribute* attr = test::GetValue<Attribute>(&table, "android:attr/foo");
ASSERT_THAT(attr, NotNull());
EXPECT_TRUE(attr->IsWeak());
- ASSERT_TRUE(table.AddResource(
- test::ParseNameOrDie("android:attr/foo"), ConfigDescription{}, "",
- util::make_unique<Attribute>(false), test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(test::ParseNameOrDie("android:attr/foo"), ConfigDescription{}, "",
+ util::make_unique<Attribute>(), test::GetDiagnostics()));
attr = test::GetValue<Attribute>(&table, "android:attr/foo");
ASSERT_THAT(attr, NotNull());
EXPECT_FALSE(attr->IsWeak());
}
+TEST(ResourceTableTest, AllowCompatibleDuplicateAttributes) {
+ ResourceTable table;
+
+ const ResourceName name = test::ParseNameOrDie("android:attr/foo");
+ Attribute attr_one(android::ResTable_map::TYPE_STRING);
+ attr_one.SetWeak(true);
+ Attribute attr_two(android::ResTable_map::TYPE_STRING | android::ResTable_map::TYPE_REFERENCE);
+ attr_two.SetWeak(true);
+
+ ASSERT_TRUE(table.AddResource(name, ConfigDescription{}, "",
+ util::make_unique<Attribute>(attr_one), test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(name, ConfigDescription{}, "",
+ util::make_unique<Attribute>(attr_two), test::GetDiagnostics()));
+}
+
TEST(ResourceTableTest, ProductVaryingValues) {
ResourceTable table;
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index e637c3e..cb786d3 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -179,12 +179,11 @@
}
TEST(ResourceUtilsTest, ParseEmptyFlag) {
- std::unique_ptr<Attribute> attr =
- test::AttributeBuilder(false)
- .SetTypeMask(ResTable_map::TYPE_FLAGS)
- .AddItem("one", 0x01)
- .AddItem("two", 0x02)
- .Build();
+ std::unique_ptr<Attribute> attr = test::AttributeBuilder()
+ .SetTypeMask(ResTable_map::TYPE_FLAGS)
+ .AddItem("one", 0x01)
+ .AddItem("two", 0x02)
+ .Build();
std::unique_ptr<BinaryPrimitive> result = ResourceUtils::TryParseFlagSymbol(attr.get(), "");
ASSERT_THAT(result, NotNull());
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index a782cd3..77cee06 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -507,17 +507,10 @@
}
}
-Attribute::Attribute()
- : type_mask(0u),
- min_int(std::numeric_limits<int32_t>::min()),
- max_int(std::numeric_limits<int32_t>::max()) {
-}
-
-Attribute::Attribute(bool w, uint32_t t)
+Attribute::Attribute(uint32_t t)
: type_mask(t),
min_int(std::numeric_limits<int32_t>::min()),
max_int(std::numeric_limits<int32_t>::max()) {
- weak_ = w;
}
std::ostream& operator<<(std::ostream& out, const Attribute::Symbol& s) {
@@ -568,6 +561,20 @@
});
}
+bool Attribute::IsCompatibleWith(const Attribute& attr) const {
+ // If the high bits are set on any of these attribute type masks, then they are incompatible.
+ // We don't check that flags and enums are identical.
+ if ((type_mask & ~android::ResTable_map::TYPE_ANY) != 0 ||
+ (attr.type_mask & ~android::ResTable_map::TYPE_ANY) != 0) {
+ return false;
+ }
+
+ // Every attribute accepts a reference.
+ uint32_t this_type_mask = type_mask | android::ResTable_map::TYPE_REFERENCE;
+ uint32_t that_type_mask = attr.type_mask | android::ResTable_map::TYPE_REFERENCE;
+ return this_type_mask == that_type_mask;
+}
+
Attribute* Attribute::Clone(StringPool* /*new_pool*/) const {
return new Attribute(*this);
}
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index b2ec8bdd..6371c4c 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -300,10 +300,15 @@
int32_t max_int;
std::vector<Symbol> symbols;
- Attribute();
- explicit Attribute(bool w, uint32_t t = 0u);
+ explicit Attribute(uint32_t t = 0u);
bool Equals(const Value* value) const override;
+
+ // Returns true if this Attribute's format is compatible with the given Attribute. The basic
+ // rule is that TYPE_REFERENCE can be ignored for both of the Attributes, and TYPE_FLAGS and
+ // TYPE_ENUMS are never compatible.
+ bool IsCompatibleWith(const Attribute& attr) const;
+
Attribute* Clone(StringPool* new_pool) const override;
std::string MaskString() const;
void Print(std::ostream* out) const override;
diff --git a/tools/aapt2/ResourceValues_test.cpp b/tools/aapt2/ResourceValues_test.cpp
index a80a9dc..c4a1108 100644
--- a/tools/aapt2/ResourceValues_test.cpp
+++ b/tools/aapt2/ResourceValues_test.cpp
@@ -24,6 +24,18 @@
namespace aapt {
+namespace {
+
+// Attribute types.
+constexpr const uint32_t TYPE_DIMENSION = android::ResTable_map::TYPE_DIMENSION;
+constexpr const uint32_t TYPE_ENUM = android::ResTable_map::TYPE_ENUM;
+constexpr const uint32_t TYPE_FLAGS = android::ResTable_map::TYPE_FLAGS;
+constexpr const uint32_t TYPE_INTEGER = android::ResTable_map::TYPE_INTEGER;
+constexpr const uint32_t TYPE_REFERENCE = android::Res_value::TYPE_REFERENCE;
+constexpr const uint32_t TYPE_STRING = android::ResTable_map::TYPE_STRING;
+
+} // namespace
+
TEST(ResourceValuesTest, PluralEquals) {
StringPool pool;
@@ -206,23 +218,19 @@
android::Res_value value = {};
ASSERT_TRUE(Reference().Flatten(&value));
- EXPECT_EQ(android::Res_value::TYPE_REFERENCE, value.dataType);
- EXPECT_EQ(0x0u, value.data);
+ EXPECT_THAT(value.dataType, Eq(android::Res_value::TYPE_REFERENCE));
+ EXPECT_THAT(value.data, Eq(0u));
}
TEST(ResourcesValuesTest, AttributeMatches) {
- constexpr const uint32_t TYPE_DIMENSION = android::ResTable_map::TYPE_DIMENSION;
- constexpr const uint32_t TYPE_ENUM = android::ResTable_map::TYPE_ENUM;
- constexpr const uint32_t TYPE_FLAGS = android::ResTable_map::TYPE_FLAGS;
- constexpr const uint32_t TYPE_INTEGER = android::ResTable_map::TYPE_INTEGER;
constexpr const uint8_t TYPE_INT_DEC = android::Res_value::TYPE_INT_DEC;
- Attribute attr1(false /*weak*/, TYPE_DIMENSION);
+ Attribute attr1(TYPE_DIMENSION);
EXPECT_FALSE(attr1.Matches(*ResourceUtils::TryParseColor("#7fff00")));
EXPECT_TRUE(attr1.Matches(*ResourceUtils::TryParseFloat("23dp")));
EXPECT_TRUE(attr1.Matches(*ResourceUtils::TryParseReference("@android:string/foo")));
- Attribute attr2(false /*weak*/, TYPE_INTEGER | TYPE_ENUM);
+ Attribute attr2(TYPE_INTEGER | TYPE_ENUM);
attr2.min_int = 0;
attr2.symbols.push_back(Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/foo")),
static_cast<uint32_t>(-1)});
@@ -231,7 +239,7 @@
EXPECT_TRUE(attr2.Matches(BinaryPrimitive(TYPE_INT_DEC, 1u)));
EXPECT_FALSE(attr2.Matches(BinaryPrimitive(TYPE_INT_DEC, static_cast<uint32_t>(-2))));
- Attribute attr3(false /*weak*/, TYPE_INTEGER | TYPE_FLAGS);
+ Attribute attr3(TYPE_INTEGER | TYPE_FLAGS);
attr3.max_int = 100;
attr3.symbols.push_back(
Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/foo")), 0x01u});
@@ -251,11 +259,33 @@
// Not a flag and greater than max_int.
EXPECT_FALSE(attr3.Matches(BinaryPrimitive(TYPE_INT_DEC, 127u)));
- Attribute attr4(false /*weak*/, TYPE_ENUM);
+ Attribute attr4(TYPE_ENUM);
attr4.symbols.push_back(
Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/foo")), 0x01u});
EXPECT_TRUE(attr4.Matches(BinaryPrimitive(TYPE_INT_DEC, 0x01u)));
EXPECT_FALSE(attr4.Matches(BinaryPrimitive(TYPE_INT_DEC, 0x02u)));
}
+TEST(ResourcesValuesTest, AttributeIsCompatible) {
+ Attribute attr_one(TYPE_STRING | TYPE_REFERENCE);
+ Attribute attr_two(TYPE_STRING);
+ Attribute attr_three(TYPE_ENUM);
+ Attribute attr_four(TYPE_REFERENCE);
+
+ EXPECT_TRUE(attr_one.IsCompatibleWith(attr_one));
+ EXPECT_TRUE(attr_one.IsCompatibleWith(attr_two));
+ EXPECT_FALSE(attr_one.IsCompatibleWith(attr_three));
+ EXPECT_FALSE(attr_one.IsCompatibleWith(attr_four));
+
+ EXPECT_TRUE(attr_two.IsCompatibleWith(attr_one));
+ EXPECT_TRUE(attr_two.IsCompatibleWith(attr_two));
+ EXPECT_FALSE(attr_two.IsCompatibleWith(attr_three));
+ EXPECT_FALSE(attr_two.IsCompatibleWith(attr_four));
+
+ EXPECT_FALSE(attr_three.IsCompatibleWith(attr_one));
+ EXPECT_FALSE(attr_three.IsCompatibleWith(attr_two));
+ EXPECT_FALSE(attr_three.IsCompatibleWith(attr_three));
+ EXPECT_FALSE(attr_three.IsCompatibleWith(attr_four));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 3bec082..101f74e 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -772,8 +772,8 @@
if (*type != ResourceType::kRaw) {
if (path_data.extension == "xml") {
compile_func = &CompileXml;
- } else if (!options.no_png_crunch &&
- (path_data.extension == "png" || path_data.extension == "9.png")) {
+ } else if ((!options.no_png_crunch && path_data.extension == "png") ||
+ path_data.extension == "9.png") {
compile_func = &CompilePng;
}
}
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 964dacf..d80307c 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -47,7 +47,7 @@
virtual bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
IArchiveWriter* writer) = 0;
virtual bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) = 0;
- virtual bool SerializeFile(const FileReference* file, IArchiveWriter* writer) = 0;
+ virtual bool SerializeFile(FileReference* file, IArchiveWriter* writer) = 0;
virtual ~IApkSerializer() = default;
@@ -65,19 +65,15 @@
}
if (apk->GetResourceTable() != nullptr) {
- // Resource table
- if (!serializer->SerializeTable(apk->GetResourceTable(), writer)) {
- context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
- << "failed to serialize the resource table");
- return false;
- }
+ // The table might be modified by below code.
+ auto converted_table = apk->GetResourceTable();
// Resources
- for (const auto& package : apk->GetResourceTable()->packages) {
+ for (const auto& package : converted_table->packages) {
for (const auto& type : package->types) {
for (const auto& entry : type->entries) {
for (const auto& config_value : entry->values) {
- const FileReference* file = ValueCast<FileReference>(config_value->value.get());
+ FileReference* file = ValueCast<FileReference>(config_value->value.get());
if (file != nullptr) {
if (file->file == nullptr) {
context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
@@ -95,6 +91,13 @@
} // entry
} // type
} // package
+
+ // Converted resource table
+ if (!serializer->SerializeTable(converted_table, writer)) {
+ context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+ << "failed to serialize the resource table");
+ return false;
+ }
}
// Other files
@@ -158,7 +161,7 @@
ArchiveEntry::kAlign, writer);
}
- bool SerializeFile(const FileReference* file, IArchiveWriter* writer) override {
+ bool SerializeFile(FileReference* file, IArchiveWriter* writer) override {
if (file->type == ResourceFile::Type::kProtoXml) {
unique_ptr<io::InputStream> in = file->file->OpenInputStream();
if (in == nullptr) {
@@ -189,6 +192,8 @@
<< "failed to serialize to binary XML: " << *file->path);
return false;
}
+
+ file->type = ResourceFile::Type::kBinaryXml;
} else {
if (!io::CopyFileToArchivePreserveCompression(context_, file->file, *file->path, writer)) {
context_->GetDiagnostics()->Error(DiagMessage(source_)
@@ -225,7 +230,7 @@
ArchiveEntry::kCompress, writer);
}
- bool SerializeFile(const FileReference* file, IArchiveWriter* writer) override {
+ bool SerializeFile(FileReference* file, IArchiveWriter* writer) override {
if (file->type == ResourceFile::Type::kBinaryXml) {
std::unique_ptr<io::IData> data = file->file->OpenAsData();
if (!data) {
@@ -247,6 +252,8 @@
<< "failed to serialize to proto XML: " << *file->path);
return false;
}
+
+ file->type = ResourceFile::Type::kProtoXml;
} else {
if (!io::CopyFileToArchivePreserveCompression(context_, file->file, *file->path, writer)) {
context_->GetDiagnostics()->Error(DiagMessage(source_)
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 72e07dc..c9e272c 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -388,10 +388,8 @@
// generated from the attribute definitions themselves (b/62028956).
if (const SymbolTable::Symbol* s = symm->FindById(R::attr::paddingHorizontal)) {
std::vector<ReplacementAttr> replacements{
- {"paddingLeft", R::attr::paddingLeft,
- Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
- {"paddingRight", R::attr::paddingRight,
- Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
+ {"paddingLeft", R::attr::paddingLeft, Attribute(android::ResTable_map::TYPE_DIMENSION)},
+ {"paddingRight", R::attr::paddingRight, Attribute(android::ResTable_map::TYPE_DIMENSION)},
};
rules_[R::attr::paddingHorizontal] =
util::make_unique<DegradeToManyRule>(std::move(replacements));
@@ -399,10 +397,8 @@
if (const SymbolTable::Symbol* s = symm->FindById(R::attr::paddingVertical)) {
std::vector<ReplacementAttr> replacements{
- {"paddingTop", R::attr::paddingTop,
- Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
- {"paddingBottom", R::attr::paddingBottom,
- Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
+ {"paddingTop", R::attr::paddingTop, Attribute(android::ResTable_map::TYPE_DIMENSION)},
+ {"paddingBottom", R::attr::paddingBottom, Attribute(android::ResTable_map::TYPE_DIMENSION)},
};
rules_[R::attr::paddingVertical] =
util::make_unique<DegradeToManyRule>(std::move(replacements));
@@ -411,9 +407,9 @@
if (const SymbolTable::Symbol* s = symm->FindById(R::attr::layout_marginHorizontal)) {
std::vector<ReplacementAttr> replacements{
{"layout_marginLeft", R::attr::layout_marginLeft,
- Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
+ Attribute(android::ResTable_map::TYPE_DIMENSION)},
{"layout_marginRight", R::attr::layout_marginRight,
- Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
+ Attribute(android::ResTable_map::TYPE_DIMENSION)},
};
rules_[R::attr::layout_marginHorizontal] =
util::make_unique<DegradeToManyRule>(std::move(replacements));
@@ -422,9 +418,9 @@
if (const SymbolTable::Symbol* s = symm->FindById(R::attr::layout_marginVertical)) {
std::vector<ReplacementAttr> replacements{
{"layout_marginTop", R::attr::layout_marginTop,
- Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
+ Attribute(android::ResTable_map::TYPE_DIMENSION)},
{"layout_marginBottom", R::attr::layout_marginBottom,
- Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
+ Attribute(android::ResTable_map::TYPE_DIMENSION)},
};
rules_[R::attr::layout_marginVertical] =
util::make_unique<DegradeToManyRule>(std::move(replacements));
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 8d079ff..8215ddf 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -504,8 +504,8 @@
std::unique_ptr<Attribute> BinaryResourceParser::ParseAttr(const ResourceNameRef& name,
const ConfigDescription& config,
const ResTable_map_entry* map) {
- const bool is_weak = (util::DeviceToHost16(map->flags) & ResTable_entry::FLAG_WEAK) != 0;
- std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(is_weak);
+ std::unique_ptr<Attribute> attr = util::make_unique<Attribute>();
+ attr->SetWeak((util::DeviceToHost16(map->flags) & ResTable_entry::FLAG_WEAK) != 0);
// First we must discover what type of attribute this is. Find the type mask.
auto type_mask_iter = std::find_if(begin(map), end(map), [](const ResTable_map& entry) -> bool {
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index 51ccdc7..bab7010 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -215,7 +215,7 @@
}
TEST_F(TableFlattenerTest, FlattenMinMaxAttributes) {
- Attribute attr(false);
+ Attribute attr;
attr.type_mask = android::ResTable_map::TYPE_INTEGER;
attr.min_int = 10;
attr.max_int = 23;
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index 3d6975d..d8635a9 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -635,8 +635,7 @@
switch (pb_compound_value.value_case()) {
case pb::CompoundValue::kAttr: {
const pb::Attribute& pb_attr = pb_compound_value.attr();
- std::unique_ptr<Attribute> attr = util::make_unique<Attribute>();
- attr->type_mask = pb_attr.format_flags();
+ std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(pb_attr.format_flags());
attr->min_int = pb_attr.min_int();
attr->max_int = pb_attr.max_int();
for (const pb::Attribute_Symbol& pb_symbol : pb_attr.symbol()) {
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index d7f83fd..ccba5c6 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -180,7 +180,7 @@
attr.name = "name";
attr.namespace_uri = xml::kSchemaAndroid;
attr.value = "23dp";
- attr.compiled_attribute = xml::AaptAttribute({}, ResourceId(0x01010000));
+ attr.compiled_attribute = xml::AaptAttribute(Attribute{}, ResourceId(0x01010000));
attr.compiled_value =
ResourceUtils::TryParseItemForAttribute(attr.value, android::ResTable_map::TYPE_DIMENSION);
attr.compiled_value->SetSource(Source().WithLine(25));
diff --git a/tools/aapt2/integration-tests/BasicTest/Android.mk b/tools/aapt2/integration-tests/BasicTest/Android.mk
new file mode 100644
index 0000000..6d2aac6
--- /dev/null
+++ b/tools/aapt2/integration-tests/BasicTest/Android.mk
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2017 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_PACKAGE_NAME := AaptBasicTest
+LOCAL_MODULE_TAGS := tests
+include $(BUILD_PACKAGE)
diff --git a/tools/aapt2/integration-tests/BasicTest/AndroidManifest.xml b/tools/aapt2/integration-tests/BasicTest/AndroidManifest.xml
new file mode 100644
index 0000000..3743c40
--- /dev/null
+++ b/tools/aapt2/integration-tests/BasicTest/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.aapt.basic">
+
+ <uses-sdk android:minSdkVersion="21" />
+</manifest>
diff --git a/tools/aapt2/integration-tests/BasicTest/res/values/values.xml b/tools/aapt2/integration-tests/BasicTest/res/values/values.xml
new file mode 100644
index 0000000..8d6bb43
--- /dev/null
+++ b/tools/aapt2/integration-tests/BasicTest/res/values/values.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<resources>
+ <declare-styleable name="AttrConflictStyleableOne">
+ <attr name="format_conflict" format="string|reference" />
+ </declare-styleable>
+
+ <declare-styleable name="AttrConflictStyleableTwo">
+ <attr name="format_conflict" format="string" />
+ </declare-styleable>
+</resources>
diff --git a/tools/aapt2/java/ClassDefinition.cpp b/tools/aapt2/java/ClassDefinition.cpp
index b692ccf..f5f5b05 100644
--- a/tools/aapt2/java/ClassDefinition.cpp
+++ b/tools/aapt2/java/ClassDefinition.cpp
@@ -45,8 +45,18 @@
Result result = Result::kAdded;
auto iter = indexed_members_.find(member->GetName());
if (iter != indexed_members_.end()) {
- // Overwrite the entry.
- ordered_members_[iter->second].reset();
+ // Overwrite the entry. Be careful, as the key in indexed_members_ is actually memory owned
+ // by the value at ordered_members_[index]. Since overwriting a value for a key doesn't replace
+ // the key (the initial key inserted into the unordered_map is kept), we must erase and then
+ // insert a new key, whose memory is being kept around. We do all this to avoid using more
+ // memory for each key.
+ size_t index = iter->second;
+
+ // Erase the key + value from the map.
+ indexed_members_.erase(iter);
+
+ // Now clear the memory that was backing the key (now erased).
+ ordered_members_[index].reset();
result = Result::kOverridden;
}
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index 5beb594..e449546 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -57,7 +57,7 @@
.SetPackageId("android", 0x01)
.AddSimple("android:id/hey-man", ResourceId(0x01020000))
.AddValue("android:attr/cool.attr", ResourceId(0x01010000),
- test::AttributeBuilder(false).Build())
+ test::AttributeBuilder().Build())
.AddValue("android:styleable/hey.dude", ResourceId(0x01030000),
test::StyleableBuilder()
.AddItem("android:attr/cool.attr", ResourceId(0x01010000))
@@ -229,10 +229,8 @@
test::ResourceTableBuilder()
.SetPackageId("android", 0x01)
.SetPackageId("com.lib", 0x02)
- .AddValue("android:attr/bar", ResourceId(0x01010000),
- test::AttributeBuilder(false).Build())
- .AddValue("com.lib:attr/bar", ResourceId(0x02010000),
- test::AttributeBuilder(false).Build())
+ .AddValue("android:attr/bar", ResourceId(0x01010000), test::AttributeBuilder().Build())
+ .AddValue("com.lib:attr/bar", ResourceId(0x02010000), test::AttributeBuilder().Build())
.AddValue("android:styleable/foo", ResourceId(0x01030000),
test::StyleableBuilder()
.AddItem("android:attr/bar", ResourceId(0x01010000))
@@ -290,7 +288,7 @@
TEST(JavaClassGeneratorTest, CommentsForEnumAndFlagAttributesArePresent) {}
TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent) {
- Attribute attr(false);
+ Attribute attr;
attr.SetComment(StringPiece("This is an attribute"));
Styleable styleable;
@@ -375,7 +373,7 @@
}
TEST(JavaClassGeneratorTest, CommentsForRemovedAttributesAreNotPresentInClass) {
- Attribute attr(false);
+ Attribute attr;
attr.SetComment(StringPiece("removed"));
std::unique_ptr<ResourceTable> table =
@@ -413,7 +411,7 @@
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("android", 0x00)
- .AddValue("android:attr/foo", ResourceId(0x00010000), util::make_unique<Attribute>(false))
+ .AddValue("android:attr/foo", ResourceId(0x00010000), util::make_unique<Attribute>())
.AddValue("android:id/foo", ResourceId(0x00020000), util::make_unique<Id>())
.AddValue(
"android:style/foo", ResourceId(0x00030000),
diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp
index c324238..f4e10ab 100644
--- a/tools/aapt2/java/ManifestClassGenerator_test.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp
@@ -129,13 +129,16 @@
std::unique_ptr<xml::XmlResource> manifest = test::BuildXmlDom(R"(
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<permission android:name="android.permission.ACCESS_INTERNET" />
- <permission android:name="com.android.aapt.test.ACCESS_INTERNET" />
+ <permission android:name="com.android.sample.ACCESS_INTERNET" />
+ <permission android:name="com.android.permission.UNRELATED_PERMISSION" />
+ <permission android:name="com.android.aapt.test.ACCESS_INTERNET" /> -->
</manifest>)");
std::string actual;
ASSERT_TRUE(GetManifestClassText(context.get(), manifest.get(), &actual));
EXPECT_THAT(actual, HasSubstr("ACCESS_INTERNET=\"com.android.aapt.test.ACCESS_INTERNET\";"));
EXPECT_THAT(actual, Not(HasSubstr("ACCESS_INTERNET=\"android.permission.ACCESS_INTERNET\";")));
+ EXPECT_THAT(actual, Not(HasSubstr("ACCESS_INTERNET=\"com.android.sample.ACCESS_INTERNET\";")));
}
static ::testing::AssertionResult GetManifestClassText(IAaptContext* context, xml::XmlResource* res,
diff --git a/tools/aapt2/link/XmlCompatVersioner_test.cpp b/tools/aapt2/link/XmlCompatVersioner_test.cpp
index 29ad25f..1ed4536 100644
--- a/tools/aapt2/link/XmlCompatVersioner_test.cpp
+++ b/tools/aapt2/link/XmlCompatVersioner_test.cpp
@@ -54,17 +54,17 @@
.AddSymbolSource(
test::StaticSymbolSourceBuilder()
.AddPublicSymbol("android:attr/paddingLeft", R::attr::paddingLeft,
- util::make_unique<Attribute>(false, TYPE_DIMENSION))
+ util::make_unique<Attribute>(TYPE_DIMENSION))
.AddPublicSymbol("android:attr/paddingRight", R::attr::paddingRight,
- util::make_unique<Attribute>(false, TYPE_DIMENSION))
+ util::make_unique<Attribute>(TYPE_DIMENSION))
.AddPublicSymbol("android:attr/progressBarPadding", R::attr::progressBarPadding,
- util::make_unique<Attribute>(false, TYPE_DIMENSION))
+ util::make_unique<Attribute>(TYPE_DIMENSION))
.AddPublicSymbol("android:attr/paddingStart", R::attr::paddingStart,
- util::make_unique<Attribute>(false, TYPE_DIMENSION))
+ util::make_unique<Attribute>(TYPE_DIMENSION))
.AddPublicSymbol("android:attr/paddingHorizontal", R::attr::paddingHorizontal,
- util::make_unique<Attribute>(false, TYPE_DIMENSION))
+ util::make_unique<Attribute>(TYPE_DIMENSION))
.AddSymbol("com.app:attr/foo", ResourceId(0x7f010000),
- util::make_unique<Attribute>(false, TYPE_STRING))
+ util::make_unique<Attribute>(TYPE_STRING))
.Build())
.Build();
}
@@ -126,9 +126,8 @@
XmlCompatVersioner::Rules rules;
rules[R::attr::paddingHorizontal] =
util::make_unique<DegradeToManyRule>(std::vector<ReplacementAttr>(
- {ReplacementAttr{"paddingLeft", R::attr::paddingLeft, Attribute(false, TYPE_DIMENSION)},
- ReplacementAttr{"paddingRight", R::attr::paddingRight,
- Attribute(false, TYPE_DIMENSION)}}));
+ {ReplacementAttr{"paddingLeft", R::attr::paddingLeft, Attribute(TYPE_DIMENSION)},
+ ReplacementAttr{"paddingRight", R::attr::paddingRight, Attribute(TYPE_DIMENSION)}}));
const util::Range<ApiVersion> api_range{SDK_GINGERBREAD, SDK_O + 1};
@@ -187,12 +186,11 @@
XmlCompatVersioner::Rules rules;
rules[R::attr::progressBarPadding] =
util::make_unique<DegradeToManyRule>(std::vector<ReplacementAttr>(
- {ReplacementAttr{"paddingLeft", R::attr::paddingLeft, Attribute(false, TYPE_DIMENSION)},
- ReplacementAttr{"paddingRight", R::attr::paddingRight,
- Attribute(false, TYPE_DIMENSION)}}));
+ {ReplacementAttr{"paddingLeft", R::attr::paddingLeft, Attribute(TYPE_DIMENSION)},
+ ReplacementAttr{"paddingRight", R::attr::paddingRight, Attribute(TYPE_DIMENSION)}}));
rules[R::attr::paddingHorizontal] =
util::make_unique<DegradeToManyRule>(std::vector<ReplacementAttr>({ReplacementAttr{
- "progressBarPadding", R::attr::progressBarPadding, Attribute(false, TYPE_DIMENSION)}}));
+ "progressBarPadding", R::attr::progressBarPadding, Attribute(TYPE_DIMENSION)}}));
const util::Range<ApiVersion> api_range{SDK_GINGERBREAD, SDK_O + 1};
@@ -267,9 +265,8 @@
XmlCompatVersioner::Rules rules;
rules[R::attr::paddingHorizontal] =
util::make_unique<DegradeToManyRule>(std::vector<ReplacementAttr>(
- {ReplacementAttr{"paddingLeft", R::attr::paddingLeft, Attribute(false, TYPE_DIMENSION)},
- ReplacementAttr{"paddingRight", R::attr::paddingRight,
- Attribute(false, TYPE_DIMENSION)}}));
+ {ReplacementAttr{"paddingLeft", R::attr::paddingLeft, Attribute(TYPE_DIMENSION)},
+ ReplacementAttr{"paddingRight", R::attr::paddingRight, Attribute(TYPE_DIMENSION)}}));
const util::Range<ApiVersion> api_range{SDK_GINGERBREAD, SDK_O + 1};
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index 8852c8e..160ff92 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -79,16 +79,15 @@
void Visit(xml::Element* el) override {
// The default Attribute allows everything except enums or flags.
- constexpr const static uint32_t kDefaultTypeMask =
- 0xffffffffu & ~(android::ResTable_map::TYPE_ENUM | android::ResTable_map::TYPE_FLAGS);
- const static Attribute kDefaultAttribute(true /* weak */, kDefaultTypeMask);
+ Attribute default_attribute(android::ResTable_map::TYPE_ANY);
+ default_attribute.SetWeak(true);
const Source source = source_.WithLine(el->line_number);
for (xml::Attribute& attr : el->attributes) {
// If the attribute has no namespace, interpret values as if
// they were assigned to the default Attribute.
- const Attribute* attribute = &kDefaultAttribute;
+ const Attribute* attribute = &default_attribute;
if (Maybe<xml::ExtractedPackage> maybe_package =
xml::ExtractPackageFromNamespace(attr.namespace_uri)) {
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 3cae0e8..2e97a2f 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -243,7 +243,7 @@
// Check to see if it is an attribute.
for (size_t i = 0; i < (size_t)count; i++) {
if (entry[i].map.name.ident == android::ResTable_map::ATTR_TYPE) {
- s->attribute = std::make_shared<Attribute>(false, entry[i].map.value.data);
+ s->attribute = std::make_shared<Attribute>(entry[i].map.value.data);
break;
}
}
diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp
index 495a48a..c4eab12 100644
--- a/tools/aapt2/test/Builders.cpp
+++ b/tools/aapt2/test/Builders.cpp
@@ -156,8 +156,8 @@
return util::make_unique<BinaryPrimitive>(value);
}
-AttributeBuilder::AttributeBuilder(bool weak) : attr_(util::make_unique<Attribute>(weak)) {
- attr_->type_mask = android::ResTable_map::TYPE_ANY;
+AttributeBuilder::AttributeBuilder()
+ : attr_(util::make_unique<Attribute>(android::ResTable_map::TYPE_ANY)) {
}
AttributeBuilder& AttributeBuilder::SetTypeMask(uint32_t typeMask) {
@@ -165,6 +165,11 @@
return *this;
}
+AttributeBuilder& AttributeBuilder::SetWeak(bool weak) {
+ attr_->SetWeak(weak);
+ return *this;
+}
+
AttributeBuilder& AttributeBuilder::AddItem(const StringPiece& name, uint32_t value) {
attr_->symbols.push_back(
Attribute::Symbol{Reference(ResourceName({}, ResourceType::kId, name)), value});
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 0d7451b..fd5262a 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -113,8 +113,9 @@
class AttributeBuilder {
public:
- explicit AttributeBuilder(bool weak = false);
+ AttributeBuilder();
AttributeBuilder& SetTypeMask(uint32_t typeMask);
+ AttributeBuilder& SetWeak(bool weak);
AttributeBuilder& AddItem(const android::StringPiece& name, uint32_t value);
std::unique_ptr<Attribute> Build();
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 421e545..399b0c6 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -100,9 +100,11 @@
self.typ = raw[0]
self.name = raw[1]
self.args = []
+ self.throws = []
+ target = self.args
for r in raw[2:]:
- if r == "throws": break
- self.args.append(r)
+ if r == "throws": target = self.throws
+ else: target.append(r)
# identity for compat purposes
ident = self.raw
@@ -391,7 +393,7 @@
prefix = clazz.pkg.name + ".action"
expected = prefix + "." + f.name[7:]
if f.value != expected:
- error(clazz, f, "C4", "Inconsistent action value; expected %s" % (expected))
+ error(clazz, f, "C4", "Inconsistent action value; expected '%s'" % (expected))
def verify_extras(clazz):
@@ -421,7 +423,7 @@
prefix = clazz.pkg.name + ".extra"
expected = prefix + "." + f.name[6:]
if f.value != expected:
- error(clazz, f, "C4", "Inconsistent extra value; expected %s" % (expected))
+ error(clazz, f, "C4", "Inconsistent extra value; expected '%s'" % (expected))
def verify_equals(clazz):
@@ -450,6 +452,10 @@
(" final deprecated class " not in clazz.raw)):
error(clazz, None, "FW8", "Parcelable classes must be final")
+ for c in clazz.ctors:
+ if c.args == ["android.os.Parcel"]:
+ error(clazz, c, "FW3", "Parcelable inflation is exposed through CREATOR, not raw constructors")
+
def verify_protected(clazz):
"""Verify that no protected methods or fields are allowed."""
@@ -572,7 +578,7 @@
if f.name == "SERVICE_INTERFACE":
found = True
if f.value != clazz.fullname:
- error(clazz, f, "C4", "Inconsistent interface constant; expected %s" % (clazz.fullname))
+ error(clazz, f, "C4", "Inconsistent interface constant; expected '%s'" % (clazz.fullname))
if "extends android.content.ContentProvider" in clazz.raw:
test_methods = True
@@ -584,7 +590,7 @@
if f.name == "PROVIDER_INTERFACE":
found = True
if f.value != clazz.fullname:
- error(clazz, f, "C4", "Inconsistent interface constant; expected %s" % (clazz.fullname))
+ error(clazz, f, "C4", "Inconsistent interface constant; expected '%s'" % (clazz.fullname))
if "extends android.content.BroadcastReceiver" in clazz.raw:
test_methods = True
@@ -764,15 +770,19 @@
def verify_exception(clazz):
"""Verifies that methods don't throw generic exceptions."""
for m in clazz.methods:
- if "throws java.lang.Exception" in m.raw or "throws java.lang.Throwable" in m.raw or "throws java.lang.Error" in m.raw:
- error(clazz, m, "S1", "Methods must not throw generic exceptions")
+ for t in m.throws:
+ if t in ["java.lang.Exception", "java.lang.Throwable", "java.lang.Error"]:
+ error(clazz, m, "S1", "Methods must not throw generic exceptions")
- if "throws android.os.RemoteException" in m.raw:
- if clazz.name == "android.content.ContentProviderClient": continue
- if clazz.name == "android.os.Binder": continue
- if clazz.name == "android.os.IBinder": continue
+ if t in ["android.os.RemoteException"]:
+ if clazz.name == "android.content.ContentProviderClient": continue
+ if clazz.name == "android.os.Binder": continue
+ if clazz.name == "android.os.IBinder": continue
- error(clazz, m, "FW9", "Methods calling into system server should rethrow RemoteException as RuntimeException")
+ error(clazz, m, "FW9", "Methods calling into system server should rethrow RemoteException as RuntimeException")
+
+ if len(m.args) == 0 and t in ["java.lang.IllegalArgumentException", "java.lang.NullPointerException"]:
+ warn(clazz, m, "S1", "Methods taking no arguments should throw IllegalStateException")
def verify_google(clazz):
@@ -927,7 +937,8 @@
found = {}
by_name = collections.defaultdict(list)
- for m in clazz.methods:
+ examine = clazz.ctors + clazz.methods
+ for m in examine:
if m.name.startswith("unregister"): continue
if m.name.startswith("remove"): continue
if re.match("on[A-Z]+", m.name): continue
@@ -971,7 +982,7 @@
for a in m.args:
if a.endswith("Callback") or a.endswith("Callbacks") or a.endswith("Listener"):
found = True
- elif found and a != "android.os.Handler" and a != "java.util.concurrent.Executor":
+ elif found:
warn(clazz, m, "M3", "Listeners should always be at end of argument list")
@@ -1078,16 +1089,11 @@
"java.nio.BufferOverflowException",
]
- test = []
- test.extend(clazz.ctors)
- test.extend(clazz.methods)
-
- for t in test:
- if " throws " not in t.raw: continue
- throws = t.raw[t.raw.index(" throws "):]
- for b in banned:
- if b in throws:
- error(clazz, t, None, "Methods must not mention RuntimeException subclasses in throws clauses")
+ examine = clazz.ctors + clazz.methods
+ for m in examine:
+ for t in m.throws:
+ if t in banned:
+ error(clazz, m, None, "Methods must not mention RuntimeException subclasses in throws clauses")
def verify_error(clazz):
@@ -1233,6 +1239,58 @@
warn(clazz, m, None, "Method argument should be Collection<> (or subclass) instead of raw array")
+def verify_user_handle(clazz):
+ """Methods taking UserHandle should be ForUser or AsUser."""
+ if clazz.name.endswith("Listener") or clazz.name.endswith("Callback") or clazz.name.endswith("Callbacks"): return
+ if clazz.fullname == "android.app.admin.DeviceAdminReceiver": return
+ if clazz.fullname == "android.content.pm.LauncherApps": return
+ if clazz.fullname == "android.os.UserHandle": return
+ if clazz.fullname == "android.os.UserManager": return
+
+ for m in clazz.methods:
+ if m.name.endswith("AsUser") or m.name.endswith("ForUser"): continue
+ if re.match("on[A-Z]+", m.name): continue
+ if "android.os.UserHandle" in m.args:
+ warn(clazz, m, None, "Method taking UserHandle should be named 'doFooAsUser' or 'queryFooForUser'")
+
+
+def verify_params(clazz):
+ """Parameter classes should be 'Params'."""
+ if clazz.name.endswith("Params"): return
+ if clazz.fullname == "android.app.ActivityOptions": return
+ if clazz.fullname == "android.app.BroadcastOptions": return
+ if clazz.fullname == "android.os.Bundle": return
+ if clazz.fullname == "android.os.BaseBundle": return
+ if clazz.fullname == "android.os.PersistableBundle": return
+
+ bad = ["Param","Parameter","Parameters","Args","Arg","Argument","Arguments","Options","Bundle"]
+ for b in bad:
+ if clazz.name.endswith(b):
+ error(clazz, None, None, "Classes holding a set of parameters should be called 'FooParams'")
+
+
+def verify_services(clazz):
+ """Service name should be FOO_BAR_SERVICE = 'foo_bar'."""
+ if clazz.fullname != "android.content.Context": return
+
+ for f in clazz.fields:
+ if f.typ != "java.lang.String": continue
+ found = re.match(r"([A-Z_]+)_SERVICE", f.name)
+ if found:
+ expected = found.group(1).lower()
+ if f.value != expected:
+ error(clazz, f, "C4", "Inconsistent service value; expected '%s'" % (expected))
+
+
+def verify_tense(clazz):
+ """Verify tenses of method names."""
+ if clazz.fullname.startswith("android.opengl"): return
+
+ for m in clazz.methods:
+ if m.name.endswith("Enable"):
+ warn(clazz, m, None, "Unexpected tense; probably meant 'enabled'")
+
+
def examine_clazz(clazz):
"""Find all style issues in the given class."""
@@ -1290,6 +1348,10 @@
verify_member_name_not_kotlin_keyword(clazz)
verify_method_name_not_kotlin_operator(clazz)
verify_collections_over_arrays(clazz)
+ verify_user_handle(clazz)
+ verify_params(clazz)
+ verify_services(clazz)
+ verify_tense(clazz)
def examine_stream(stream):
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index 80853b1..0e57f7f 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -15,6 +15,7 @@
*/
#include "Collation.h"
+#include "frameworks/base/cmds/statsd/src/atoms.pb.h"
#include <stdio.h>
#include <map>
@@ -137,6 +138,16 @@
}
/**
+ * Gather the enums info.
+ */
+void collate_enums(const EnumDescriptor &enumDescriptor, AtomField *atomField) {
+ for (int i = 0; i < enumDescriptor.value_count(); i++) {
+ atomField->enumValues[enumDescriptor.value(i)->number()] =
+ enumDescriptor.value(i)->name().c_str();
+ }
+}
+
+/**
* Gather the info about an atom proto.
*/
int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
@@ -221,11 +232,7 @@
if (javaType == JAVA_TYPE_ENUM) {
// All enums are treated as ints when it comes to function signatures.
signature->push_back(JAVA_TYPE_INT);
- const EnumDescriptor *enumDescriptor = field->enum_type();
- for (int i = 0; i < enumDescriptor->value_count(); i++) {
- atField.enumValues[enumDescriptor->value(i)->number()] =
- enumDescriptor->value(i)->name().c_str();
- }
+ collate_enums(*field->enum_type(), &atField);
} else {
signature->push_back(javaType);
}
@@ -235,6 +242,53 @@
return errorCount;
}
+// This function flattens the fields of the AttributionNode proto in an Atom proto and generates
+// the corresponding atom decl and signature.
+bool get_non_chained_node(const Descriptor *atom, AtomDecl *atomDecl,
+ vector<java_type_t> *signature) {
+ // Build a sorted list of the fields. Descriptor has them in source file
+ // order.
+ map<int, const FieldDescriptor *> fields;
+ for (int j = 0; j < atom->field_count(); j++) {
+ const FieldDescriptor *field = atom->field(j);
+ fields[field->number()] = field;
+ }
+
+ AtomDecl attributionDecl;
+ vector<java_type_t> attributionSignature;
+ collate_atom(android::os::statsd::AttributionNode::descriptor(),
+ &attributionDecl, &attributionSignature);
+
+ // Build the type signature and the atom data.
+ bool has_attribution_node = false;
+ for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
+ it != fields.end(); it++) {
+ const FieldDescriptor *field = it->second;
+ java_type_t javaType = java_type(field);
+ if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ atomDecl->fields.insert(
+ atomDecl->fields.end(),
+ attributionDecl.fields.begin(), attributionDecl.fields.end());
+ signature->insert(
+ signature->end(),
+ attributionSignature.begin(), attributionSignature.end());
+ has_attribution_node = true;
+
+ } else {
+ AtomField atField(field->name(), javaType);
+ if (javaType == JAVA_TYPE_ENUM) {
+ // All enums are treated as ints when it comes to function signatures.
+ signature->push_back(JAVA_TYPE_INT);
+ collate_enums(*field->enum_type(), &atField);
+ } else {
+ signature->push_back(javaType);
+ }
+ atomDecl->fields.push_back(atField);
+ }
+ }
+ return has_attribution_node;
+}
+
/**
* Gather the info about the atoms.
*/
@@ -266,6 +320,13 @@
errorCount += collate_atom(atom, &atomDecl, &signature);
atoms->signatures.insert(signature);
atoms->decls.insert(atomDecl);
+
+ AtomDecl nonChainedAtomDecl(atomField->number(), atomField->name(), atom->name());
+ vector<java_type_t> nonChainedSignature;
+ if (get_non_chained_node(atom, &nonChainedAtomDecl, &nonChainedSignature)) {
+ atoms->non_chained_signatures.insert(nonChainedSignature);
+ atoms->non_chained_decls.insert(nonChainedAtomDecl);
+ }
}
if (dbg) {
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index cd0625c..0455eca 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -32,6 +32,7 @@
using std::string;
using std::vector;
using google::protobuf::Descriptor;
+using google::protobuf::FieldDescriptor;
/**
* The types for atom parameters.
@@ -93,14 +94,15 @@
struct Atoms {
set<vector<java_type_t>> signatures;
set<AtomDecl> decls;
+ set<AtomDecl> non_chained_decls;
+ set<vector<java_type_t>> non_chained_signatures;
};
/**
* Gather the information about the atoms. Returns the number of errors.
*/
int collate_atoms(const Descriptor* descriptor, Atoms* atoms);
-int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
- vector<java_type_t> *signature);
+int collate_atom(const Descriptor *atom, AtomDecl *atomDecl, vector<java_type_t> *signature);
} // namespace stats_log_api_gen
} // namespace android
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index bbe6d63..e0e6b58 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -195,6 +195,47 @@
fprintf(out, "\n");
}
+ for (set<vector<java_type_t>>::const_iterator signature = atoms.non_chained_signatures.begin();
+ signature != atoms.non_chained_signatures.end(); signature++) {
+ int argIndex;
+
+ fprintf(out, "void\n");
+ fprintf(out, "stats_write_non_chained(int32_t code");
+ argIndex = 1;
+ for (vector<java_type_t>::const_iterator arg = signature->begin();
+ arg != signature->end(); arg++) {
+ fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+ argIndex++;
+ }
+ fprintf(out, ")\n");
+
+ fprintf(out, "{\n");
+ argIndex = 1;
+ fprintf(out, " android_log_event_list event(kStatsEventTag);\n");
+ fprintf(out, " event << code;\n\n");
+ for (vector<java_type_t>::const_iterator arg = signature->begin();
+ arg != signature->end(); arg++) {
+ if (argIndex == 1) {
+ fprintf(out, " event.begin();\n\n");
+ fprintf(out, " event.begin();\n");
+ }
+ if (*arg == JAVA_TYPE_STRING) {
+ fprintf(out, " if (arg%d == NULL) {\n", argIndex);
+ fprintf(out, " arg%d = \"\";\n", argIndex);
+ fprintf(out, " }\n");
+ }
+ fprintf(out, " event << arg%d;\n", argIndex);
+ if (argIndex == 2) {
+ fprintf(out, " event.end();\n\n");
+ fprintf(out, " event.end();\n\n");
+ }
+ argIndex++;
+ }
+
+ fprintf(out, " event.write(LOG_ID_STATS);\n");
+ fprintf(out, "}\n");
+ fprintf(out, "\n");
+ }
// Print footer
fprintf(out, "\n");
fprintf(out, "} // namespace util\n");
@@ -203,6 +244,68 @@
return 0;
}
+void build_non_chained_decl_map(const Atoms& atoms,
+ std::map<int, set<AtomDecl>::const_iterator>* decl_map){
+ for (set<AtomDecl>::const_iterator atom = atoms.non_chained_decls.begin();
+ atom != atoms.non_chained_decls.end(); atom++) {
+ decl_map->insert(std::make_pair(atom->code, atom));
+ }
+}
+
+static void write_cpp_usage(
+ FILE* out, const string& method_name, const string& atom_code_name,
+ const AtomDecl& atom, const AtomDecl &attributionDecl) {
+ fprintf(out, " * Usage: %s(StatsLog.%s", method_name.c_str(), atom_code_name.c_str());
+ for (vector<AtomField>::const_iterator field = atom.fields.begin();
+ field != atom.fields.end(); field++) {
+ if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ if (chainField.javaType == JAVA_TYPE_STRING) {
+ fprintf(out, ", const std::vector<%s>& %s",
+ cpp_type_name(chainField.javaType),
+ chainField.name.c_str());
+ } else {
+ fprintf(out, ", const %s* %s, size_t %s_length",
+ cpp_type_name(chainField.javaType),
+ chainField.name.c_str(), chainField.name.c_str());
+ }
+ }
+ } else {
+ fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
+ }
+ }
+ fprintf(out, ");\n");
+}
+
+static void write_cpp_method_header(
+ FILE* out, const string& method_name, const set<vector<java_type_t>>& signatures,
+ const AtomDecl &attributionDecl) {
+ for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
+ signature != signatures.end(); signature++) {
+ fprintf(out, "void %s(int32_t code ", method_name.c_str());
+ int argIndex = 1;
+ for (vector<java_type_t>::const_iterator arg = signature->begin();
+ arg != signature->end(); arg++) {
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ if (chainField.javaType == JAVA_TYPE_STRING) {
+ fprintf(out, ", const std::vector<%s>& %s",
+ cpp_type_name(chainField.javaType), chainField.name.c_str());
+ } else {
+ fprintf(out, ", const %s* %s, size_t %s_length",
+ cpp_type_name(chainField.javaType),
+ chainField.name.c_str(), chainField.name.c_str());
+ }
+ }
+ } else {
+ fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+ }
+ argIndex++;
+ }
+ fprintf(out, ");\n");
+
+ }
+}
static int
write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
@@ -228,6 +331,9 @@
fprintf(out, " */\n");
fprintf(out, "enum {\n");
+ std::map<int, set<AtomDecl>::const_iterator> atom_code_to_non_chained_decl_map;
+ build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
+
size_t i = 0;
// Print constants
for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
@@ -236,26 +342,13 @@
fprintf(out, "\n");
fprintf(out, " /**\n");
fprintf(out, " * %s %s\n", atom->message.c_str(), atom->name.c_str());
- fprintf(out, " * Usage: stats_write(StatsLog.%s", constant.c_str());
- for (vector<AtomField>::const_iterator field = atom->fields.begin();
- field != atom->fields.end(); field++) {
- if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- for (auto chainField : attributionDecl.fields) {
- if (chainField.javaType == JAVA_TYPE_STRING) {
- fprintf(out, ", const std::vector<%s>& %s",
- cpp_type_name(chainField.javaType),
- chainField.name.c_str());
- } else {
- fprintf(out, ", const %s* %s, size_t %s_length",
- cpp_type_name(chainField.javaType),
- chainField.name.c_str(), chainField.name.c_str());
- }
- }
- } else {
- fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
- }
+ write_cpp_usage(out, "stats_write", constant, *atom, attributionDecl);
+
+ auto non_chained_decl = atom_code_to_non_chained_decl_map.find(atom->code);
+ if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
+ write_cpp_usage(out, "stats_write_non_chained", constant, *non_chained_decl->second,
+ attributionDecl);
}
- fprintf(out, ");\n");
fprintf(out, " */\n");
char const* const comma = (i == atoms.decls.size() - 1) ? "" : ",";
fprintf(out, " %s = %d%s\n", constant.c_str(), atom->code, comma);
@@ -274,30 +367,13 @@
fprintf(out, "//\n");
fprintf(out, "// Write methods\n");
fprintf(out, "//\n");
- for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
- signature != atoms.signatures.end(); signature++) {
- fprintf(out, "void stats_write(int32_t code ");
- int argIndex = 1;
- for (vector<java_type_t>::const_iterator arg = signature->begin();
- arg != signature->end(); arg++) {
- if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- for (auto chainField : attributionDecl.fields) {
- if (chainField.javaType == JAVA_TYPE_STRING) {
- fprintf(out, ", const std::vector<%s>& %s",
- cpp_type_name(chainField.javaType), chainField.name.c_str());
- } else {
- fprintf(out, ", const %s* %s, size_t %s_length",
- cpp_type_name(chainField.javaType),
- chainField.name.c_str(), chainField.name.c_str());
- }
- }
- } else {
- fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
- }
- argIndex++;
- }
- fprintf(out, ");\n");
- }
+ write_cpp_method_header(out, "stats_write", atoms.signatures, attributionDecl);
+
+ fprintf(out, "//\n");
+ fprintf(out, "// Write flattened methods\n");
+ fprintf(out, "//\n");
+ write_cpp_method_header(out, "stats_write_non_chained", atoms.non_chained_signatures,
+ attributionDecl);
fprintf(out, "\n");
fprintf(out, "} // namespace util\n");
@@ -306,6 +382,49 @@
return 0;
}
+static void write_java_usage(
+ FILE* out, const string& method_name, const string& atom_code_name,
+ const AtomDecl& atom, const AtomDecl &attributionDecl) {
+ fprintf(out, " * Usage: StatsLog.%s(StatsLog.%s",
+ method_name.c_str(), atom_code_name.c_str());
+ for (vector<AtomField>::const_iterator field = atom.fields.begin();
+ field != atom.fields.end(); field++) {
+ if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ fprintf(out, ", %s[] %s",
+ java_type_name(chainField.javaType), chainField.name.c_str());
+ }
+ } else {
+ fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
+ }
+ }
+ fprintf(out, ");\n");
+}
+
+static void write_java_method(
+ FILE* out, const string& method_name, const set<vector<java_type_t>>& signatures,
+ const AtomDecl &attributionDecl) {
+ for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
+ signature != signatures.end(); signature++) {
+ fprintf(out, " public static native void %s(int code", method_name.c_str());
+ int argIndex = 1;
+ for (vector<java_type_t>::const_iterator arg = signature->begin();
+ arg != signature->end(); arg++) {
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ fprintf(out, ", %s[] %s",
+ java_type_name(chainField.javaType), chainField.name.c_str());
+ }
+ } else {
+ fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
+ }
+ argIndex++;
+ }
+ fprintf(out, ");\n");
+ }
+}
+
+
static int
write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
{
@@ -322,6 +441,9 @@
fprintf(out, "public class StatsLogInternal {\n");
fprintf(out, " // Constants for atom codes.\n");
+ std::map<int, set<AtomDecl>::const_iterator> atom_code_to_non_chained_decl_map;
+ build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
+
// Print constants for the atom codes.
for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
atom != atoms.decls.end(); atom++) {
@@ -329,19 +451,12 @@
fprintf(out, "\n");
fprintf(out, " /**\n");
fprintf(out, " * %s %s\n", atom->message.c_str(), atom->name.c_str());
- fprintf(out, " * Usage: StatsLog.write(StatsLog.%s", constant.c_str());
- for (vector<AtomField>::const_iterator field = atom->fields.begin();
- field != atom->fields.end(); field++) {
- if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- for (auto chainField : attributionDecl.fields) {
- fprintf(out, ", %s[] %s",
- java_type_name(chainField.javaType), chainField.name.c_str());
- }
- } else {
- fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
- }
+ write_java_usage(out, "write", constant, *atom, attributionDecl);
+ auto non_chained_decl = atom_code_to_non_chained_decl_map.find(atom->code);
+ if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
+ write_java_usage(out, "write_non_chained", constant, *non_chained_decl->second,
+ attributionDecl);
}
- fprintf(out, ");\n");
fprintf(out, " */\n");
fprintf(out, " public static final int %s = %d;\n", constant.c_str(), atom->code);
}
@@ -371,24 +486,8 @@
// Print write methods
fprintf(out, " // Write methods\n");
- for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
- signature != atoms.signatures.end(); signature++) {
- fprintf(out, " public static native void write(int code");
- int argIndex = 1;
- for (vector<java_type_t>::const_iterator arg = signature->begin();
- arg != signature->end(); arg++) {
- if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- for (auto chainField : attributionDecl.fields) {
- fprintf(out, ", %s[] %s",
- java_type_name(chainField.javaType), chainField.name.c_str());
- }
- } else {
- fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
- }
- argIndex++;
- }
- fprintf(out, ");\n");
- }
+ write_java_method(out, "write", atoms.signatures, attributionDecl);
+ write_java_method(out, "write_non_chained", atoms.non_chained_signatures, attributionDecl);
fprintf(out, "}\n");
@@ -431,9 +530,9 @@
}
static string
-jni_function_name(const vector<java_type_t>& signature)
+jni_function_name(const string& method_name, const vector<java_type_t>& signature)
{
- string result("StatsLog_write");
+ string result("StatsLog_" + method_name);
for (vector<java_type_t>::const_iterator arg = signature.begin();
arg != signature.end(); arg++) {
switch (*arg) {
@@ -509,34 +608,17 @@
}
static int
-write_stats_log_jni(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
+write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp_method_name,
+ const set<vector<java_type_t>>& signatures, const AtomDecl &attributionDecl)
{
- // Print prelude
- fprintf(out, "// This file is autogenerated\n");
- fprintf(out, "\n");
-
- fprintf(out, "#include <statslog.h>\n");
- fprintf(out, "\n");
- fprintf(out, "#include <nativehelper/JNIHelp.h>\n");
- fprintf(out, "#include <nativehelper/ScopedUtfChars.h>\n");
- fprintf(out, "#include <utils/Vector.h>\n");
- fprintf(out, "#include \"core_jni_helpers.h\"\n");
- fprintf(out, "#include \"jni.h\"\n");
- fprintf(out, "\n");
- fprintf(out, "#define UNUSED __attribute__((__unused__))\n");
- fprintf(out, "\n");
-
- fprintf(out, "namespace android {\n");
- fprintf(out, "\n");
-
// Print write methods
- for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
- signature != atoms.signatures.end(); signature++) {
+ for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
+ signature != signatures.end(); signature++) {
int argIndex;
fprintf(out, "static void\n");
fprintf(out, "%s(JNIEnv* env, jobject clazz UNUSED, jint code",
- jni_function_name(*signature).c_str());
+ jni_function_name(java_method_name, *signature).c_str());
argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
@@ -624,7 +706,7 @@
// stats_write call
argIndex = 1;
- fprintf(out, " android::util::stats_write(code");
+ fprintf(out, " android::util::%s(code", cpp_method_name.c_str());
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
@@ -675,17 +757,53 @@
fprintf(out, "\n");
}
+
+ return 0;
+}
+
+void write_jni_registration(FILE* out, const string& java_method_name,
+ const set<vector<java_type_t>>& signatures, const AtomDecl &attributionDecl) {
+ for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
+ signature != signatures.end(); signature++) {
+ fprintf(out, " { \"%s\", \"%s\", (void*)%s },\n",
+ java_method_name.c_str(),
+ jni_function_signature(*signature, attributionDecl).c_str(),
+ jni_function_name(java_method_name, *signature).c_str());
+ }
+}
+
+static int
+write_stats_log_jni(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
+{
+ // Print prelude
+ fprintf(out, "// This file is autogenerated\n");
+ fprintf(out, "\n");
+
+ fprintf(out, "#include <statslog.h>\n");
+ fprintf(out, "\n");
+ fprintf(out, "#include <nativehelper/JNIHelp.h>\n");
+ fprintf(out, "#include <nativehelper/ScopedUtfChars.h>\n");
+ fprintf(out, "#include <utils/Vector.h>\n");
+ fprintf(out, "#include \"core_jni_helpers.h\"\n");
+ fprintf(out, "#include \"jni.h\"\n");
+ fprintf(out, "\n");
+ fprintf(out, "#define UNUSED __attribute__((__unused__))\n");
+ fprintf(out, "\n");
+
+ fprintf(out, "namespace android {\n");
+ fprintf(out, "\n");
+
+ write_stats_log_jni(out, "write", "stats_write", atoms.signatures, attributionDecl);
+ write_stats_log_jni(out, "write_non_chained", "stats_write_non_chained",
+ atoms.non_chained_signatures, attributionDecl);
+
// Print registration function table
fprintf(out, "/*\n");
fprintf(out, " * JNI registration.\n");
fprintf(out, " */\n");
fprintf(out, "static const JNINativeMethod gRegisterMethods[] = {\n");
- for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
- signature != atoms.signatures.end(); signature++) {
- fprintf(out, " { \"write\", \"%s\", (void*)%s },\n",
- jni_function_signature(*signature, attributionDecl).c_str(),
- jni_function_name(*signature).c_str());
- }
+ write_jni_registration(out, "write", atoms.signatures, attributionDecl);
+ write_jni_registration(out, "write_non_chained", atoms.non_chained_signatures, attributionDecl);
fprintf(out, "};\n");
fprintf(out, "\n");
@@ -699,11 +817,9 @@
fprintf(out, "\n");
fprintf(out, "} // namespace android\n");
-
return 0;
}
-
static void
print_usage()
{
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 0dd964c..70d6ce4 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -28,7 +28,6 @@
import android.net.wifi.ScanSettings;
import android.net.wifi.ScanResult;
import android.net.wifi.PasspointManagementObjectDefinition;
-import android.net.wifi.WifiConnectionStatistics;
import android.net.wifi.WifiActivityEnergyInfo;
import android.net.Network;
@@ -166,8 +165,6 @@
void enableWifiConnectivityManager(boolean enabled);
- WifiConnectionStatistics getConnectionStatistics();
-
void disableEphemeralNetwork(String SSID);
void factoryReset();
diff --git a/wifi/java/android/net/wifi/WifiConnectionStatistics.aidl b/wifi/java/android/net/wifi/WifiConnectionStatistics.aidl
deleted file mode 100644
index 601face..0000000
--- a/wifi/java/android/net/wifi/WifiConnectionStatistics.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 2014, 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.net.wifi;
-
-parcelable WifiConnectionStatistics;
diff --git a/wifi/java/android/net/wifi/WifiConnectionStatistics.java b/wifi/java/android/net/wifi/WifiConnectionStatistics.java
deleted file mode 100644
index 1120c66..0000000
--- a/wifi/java/android/net/wifi/WifiConnectionStatistics.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2014 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.net.wifi;
-
-import android.annotation.SystemApi;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import java.util.HashMap;
-
-/**
- * Wifi Connection Statistics: gather various stats regarding WiFi connections,
- * connection requests, auto-join
- * and WiFi usage.
- * @hide
- * @removed
- */
-@SystemApi
-public class WifiConnectionStatistics implements Parcelable {
- private static final String TAG = "WifiConnnectionStatistics";
-
- /**
- * history of past connection to untrusted SSID
- * Key = SSID
- * Value = num connection
- */
- public HashMap<String, WifiNetworkConnectionStatistics> untrustedNetworkHistory;
-
- // Number of time we polled the chip and were on 5GHz
- public int num5GhzConnected;
-
- // Number of time we polled the chip and were on 2.4GHz
- public int num24GhzConnected;
-
- // Number autojoin attempts
- public int numAutoJoinAttempt;
-
- // Number auto-roam attempts
- public int numAutoRoamAttempt;
-
- // Number wifimanager join attempts
- public int numWifiManagerJoinAttempt;
-
- public WifiConnectionStatistics() {
- untrustedNetworkHistory = new HashMap<String, WifiNetworkConnectionStatistics>();
- }
-
- public void incrementOrAddUntrusted(String SSID, int connection, int usage) {
- WifiNetworkConnectionStatistics stats;
- if (TextUtils.isEmpty(SSID))
- return;
- if (untrustedNetworkHistory.containsKey(SSID)) {
- stats = untrustedNetworkHistory.get(SSID);
- if (stats != null){
- stats.numConnection = connection + stats.numConnection;
- stats.numUsage = usage + stats.numUsage;
- }
- } else {
- stats = new WifiNetworkConnectionStatistics(connection, usage);
- }
- if (stats != null) {
- untrustedNetworkHistory.put(SSID, stats);
- }
- }
-
- @Override
- public String toString() {
- StringBuilder sbuf = new StringBuilder();
- sbuf.append("Connected on: 2.4Ghz=").append(num24GhzConnected);
- sbuf.append(" 5Ghz=").append(num5GhzConnected).append("\n");
- sbuf.append(" join=").append(numWifiManagerJoinAttempt);
- sbuf.append("\\").append(numAutoJoinAttempt).append("\n");
- sbuf.append(" roam=").append(numAutoRoamAttempt).append("\n");
-
- for (String Key : untrustedNetworkHistory.keySet()) {
- WifiNetworkConnectionStatistics stats = untrustedNetworkHistory.get(Key);
- if (stats != null) {
- sbuf.append(Key).append(" ").append(stats.toString()).append("\n");
- }
- }
- return sbuf.toString();
- }
-
- /** copy constructor*/
- public WifiConnectionStatistics(WifiConnectionStatistics source) {
- untrustedNetworkHistory = new HashMap<String, WifiNetworkConnectionStatistics>();
- if (source != null) {
- untrustedNetworkHistory.putAll(source.untrustedNetworkHistory);
- }
- }
-
- /** Implement the Parcelable interface */
- public int describeContents() {
- return 0;
- }
-
- /** Implement the Parcelable interface */
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(num24GhzConnected);
- dest.writeInt(num5GhzConnected);
- dest.writeInt(numAutoJoinAttempt);
- dest.writeInt(numAutoRoamAttempt);
- dest.writeInt(numWifiManagerJoinAttempt);
-
- dest.writeInt(untrustedNetworkHistory.size());
- for (String Key : untrustedNetworkHistory.keySet()) {
- WifiNetworkConnectionStatistics num = untrustedNetworkHistory.get(Key);
- dest.writeString(Key);
- dest.writeInt(num.numConnection);
- dest.writeInt(num.numUsage);
-
- }
- }
-
- /** Implement the Parcelable interface */
- public static final Creator<WifiConnectionStatistics> CREATOR =
- new Creator<WifiConnectionStatistics>() {
- public WifiConnectionStatistics createFromParcel(Parcel in) {
- WifiConnectionStatistics stats = new WifiConnectionStatistics();
- stats.num24GhzConnected = in.readInt();
- stats.num5GhzConnected = in.readInt();
- stats.numAutoJoinAttempt = in.readInt();
- stats.numAutoRoamAttempt = in.readInt();
- stats.numWifiManagerJoinAttempt = in.readInt();
- int n = in.readInt();
- while (n-- > 0) {
- String Key = in.readString();
- int numConnection = in.readInt();
- int numUsage = in.readInt();
- WifiNetworkConnectionStatistics st =
- new WifiNetworkConnectionStatistics(numConnection, numUsage);
- stats.untrustedNetworkHistory.put(Key, st);
- }
- return stats;
- }
-
- public WifiConnectionStatistics[] newArray(int size) {
- return new WifiConnectionStatistics[size];
- }
- };
-}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 70e83db..aa75a07 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -999,20 +999,6 @@
}
/**
- * @hide
- * @removed
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.READ_WIFI_CREDENTIAL)
- public WifiConnectionStatistics getConnectionStatistics() {
- try {
- return mService.getConnectionStatistics();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Returns a WifiConfiguration matching this ScanResult
*
* @param scanResult scanResult that represents the BSSID